Tomcat的非阻塞servlet是否应该同步读取和写入侦听器的方法调用?

时间:2018-11-05 13:02:45

标签: java nio servlet-3.1

我试图了解servlet中非阻塞io的工作方式。下面的代码是tomcat附带的示例,用于显示新servlet“ NumberWriter”的功能。 此Servlet具有一个“ doGet”方法和一个内部类“ NumberWriterListener”,该类正在实现读写侦听器。 如果我将System.out.println(Thread.currentThread().getId());放入doGet方法内,则对该Servlet的所有请求在控制台上显示不同的线程ID。 在NumberWriterListener类上有一条注释:“一次只应有一个容器线程调用侦听器。” 这是否意味着doGet方法应该同步?如果所有的doGet方法都在不同的线程上,并且一次只能有一个线程在调用侦听器? 如果有人可以帮助理解,我将不胜感激。 谢谢

这是servlet示例:

    @WebServlet(asyncSupported = true, urlPatterns = { "/numberwriter" })
public class NumberWriter extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

    System.out.println(Thread.currentThread().getId()+" sthread");

    resp.setContentType("text/plain");
    resp.setCharacterEncoding("UTF-8");

    // Non-blocking IO requires async
    AsyncContext ac = req.startAsync();

    // Use a single listener for read and write. Listeners often need to
    // share state to coordinate reads and writes and this is much easier as
    // a single object.
    @SuppressWarnings("unused")
    NumberWriterListener listener = new NumberWriterListener(
            ac, req.getInputStream(), resp.getOutputStream());

}


/**
 * Keep in mind that each call may well be on a different thread to the
 * previous call. Ensure that changes in values will be visible across
 * threads. There should only ever be one container thread at a time calling
 * the listener.
 */
private static class NumberWriterListener implements ReadListener,
        WriteListener {

    private static final int LIMIT =  100;

    private final AsyncContext ac;
    private final ServletInputStream sis;
    private final ServletOutputStream sos;
    private final AtomicInteger counter = new AtomicInteger(0);

    private volatile boolean readFinished = false;
    private byte[] buffer = new byte[8192];

    private NumberWriterListener(AsyncContext ac, ServletInputStream sis,
            ServletOutputStream sos) {
        this.ac = ac;
        this.sis = sis;
        this.sos = sos;

        // In Tomcat, the order the listeners are set controls the order
        // that the first calls are made. In this case, the read listener
        // will be called before the write listener.
        sis.setReadListener(this);
        sos.setWriteListener(this);
    }

    @Override
    public void onDataAvailable() throws IOException {

        // There should be no data to read

        int read = 0;
        // Loop as long as there is data to read. If isReady() returns false
        // the socket will be added to the poller and onDataAvailable() will
        // be called again as soon as there is more data to read.
        while (sis.isReady() && read > -1) {
            read = sis.read(buffer);
            if (read > 0) {
                throw new IOException("Data was present in input stream");
            }
        }
    }

    @Override
    public void onAllDataRead() throws IOException {
        readFinished = true;

        // If sos is not ready to write data, the call to isReady() will
        // register the socket with the poller which will trigger a call to
        // onWritePossible() when the socket is ready to have data written
        // to it.
        if (sos.isReady()) {
            onWritePossible();
        }
    }

    @Override
    public void onWritePossible() throws IOException {
        if (readFinished) {
            int i = counter.get();
            boolean ready = true;
            while (i < LIMIT && ready) {
                i = counter.incrementAndGet();
                String msg = String.format("%1$020d\n", Integer.valueOf(i));
                sos.write(msg.getBytes(StandardCharsets.UTF_8));
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                ready = sos.isReady();
            }

            if (i == LIMIT) {
                ac.complete();
            }
        }
    }

    @Override
    public void onError(Throwable throwable) {
        // Should probably log the throwable
        ac.complete();
    }
}
}

0 个答案:

没有答案