2

我正在开发具有以下场景的多线程 java sevlet 数据来自不同的块,为此我只需要在最后一个请求中发送响应。数据块转发给其他类以保存数据。

public class RequestController extends HttpServlet implements ResponseHandler {
    private ExecutorService pool;
    public static ConcurrentHashMap<String, HttpServletResponse> cache;

    static {
        cache = new ConcurrentHashMap<String, HttpServletResponse>();
    }

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        try {


            BufferedReader br = request.getReader();
            String msg = br.readLine();
            br.close();

            if (msg == null) {
                msg = request.getParameter("request");
                //return;
            }
            String number = msg.substring(msg.indexOf("//") + 2, msg.indexOf(";"));
            System.out.println("number = " + number);
            cache.put(number, response);
            System.out.println("Request received");
            msg = URLDecoder.decode(msg, "UTF-8");
            System.out.println(msg);
            pool.submit(new DuplicaterRequestHandler(msg, this));


        } catch (Exception e) {
            e.printStackTrace(System.out);
        } finally {
        }
    }

}

这是我的 servlet 代码。缓存是一个静态存储,我在收到所有请求后使用它来发送响应

我也有一个监听器,它告诉 sevlet 请求已完成

public interface ResponseHandler {

    public void sendResponse(String number, String data);
}

它在 RequestController 中的实现是

 @Override
    public void sendResponse(String number, String data) {

        System.out.print(number);
        System.out.println(cache.containsKey(number));


        if (cache.containsKey(number)) {
            try {
                PrintWriter pr = cache.get(number).getWriter();
                pr.println(data);
                pr.close();
                cache.remove(number);
                System.out.println("response sent.");
                System.out.println("data:" + data);
            } catch (Exception e) {
                e.printStackTrace(System.out);
                System.out.println(e.getMessage());
            }
        }
    }

一切看起来都很好,但它有时会抛出异常,而不是每次,这对我来说是未知的。

java.lang.NullPointerException
    at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:215)
    at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:462)
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:366)
    at org.apache.coyote.http11.InternalOutputBuffer$OutputStreamOutputBuffer.doWrite(InternalOutputBuffer.java:240)
    at org.apache.coyote.http11.filters.ChunkedOutputFilter.doWrite(ChunkedOutputFilter.java:119)
    at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:192)
    at org.apache.coyote.Response.doWrite(Response.java:504)
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:383)
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:342)
    at org.apache.tomcat.util.buf.IntermediateOutputStream.write(C2BConverter.java:278)
    at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:263)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:106)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:190)
    at org.apache.tomcat.util.buf.WriteConvertor.write(C2BConverter.java:242)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:111)
    at java.io.BufferedWriter.write(BufferedWriter.java:212)
    at org.apache.tomcat.util.buf.C2BConverter.convert(C2BConverter.java:132)
    at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:497)
    at org.apache.catalina.connector.CoyoteWriter.write(CoyoteWriter.java:174)
    at org.apache.catalina.connector.CoyoteWriter.write(CoyoteWriter.java:184)
    at org.apache.catalina.connector.CoyoteWriter.print(CoyoteWriter.java:242)
    at org.apache.catalina.connector.CoyoteWriter.println(CoyoteWriter.java:309)
    at duplicateserver.request.cotroller.RequestController.sendResponse(RequestController.java:132)
    at duplicateserver.request.manager.CallLogRestoreManager.processRequest(CallLogRestoreManager.java:35)
    at duplicateserver.request.handler.DuplicaterRequestHandler.run(DuplicaterRequestHandler.java:46)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:662)

如何解决?提前致谢

4

1 回答 1

2

有时抛出异常,而不是每次

这对我来说是一个线程安全问题。

您的 servlet 有一个共享的、可修改的缓存。您从缓存中删除了数字,但我没有看到任何同步块来防止竞争条件。

在缓存中找到数字后,您可能有一个线程输入该代码。另一个线程进来并删除了这个数字,所以尽管第一个线程通过了 if 测试,但当它执行 get 时,这个数字已经从缓存中消失了。

使该操作原子化,看看你是否做得更好。

@Override
    public void sendResponse(String number, String data) {

        System.out.print(number);
        System.out.println(cache.containsKey(number));

        synchronized(this) {
            if (cache.containsKey(number)) {
                try {
                    PrintWriter pr = cache.get(number).getWriter();
                    pr.println(data);
                    pr.close();
                    cache.remove(number);
                    System.out.println("response sent.");
                    System.out.println("data:" + data);
                } catch (Exception e) {
                    e.printStackTrace(System.out);
                    System.out.println(e.getMessage());
                }
            }
        }
    }
于 2013-05-22T09:36:39.737 回答