Должны ли синхронизироваться не блокирующие сервлеты tomcat для вызова методов чтения и записи? - PullRequest
0 голосов
/ 05 ноября 2018

Я пытаюсь понять, как неблокирующая работа в сервлетах. приведенный ниже код является примером, который поставляется с tomcat для демонстрации функциональности нового сервлета "NumberWriter". этот сервлет имеет один метод "doGet" и один внутренний класс NumberWriterListener, который реализует прослушиватель чтения-записи. если я помещаю System.out.println(Thread.currentThread().getId()); в метод doGet, все запросы к этому сервлету показывают другой идентификатор потока на консоли. в классе NumberWriterListener есть комментарий: «В каждый момент времени должен быть только один поток контейнера, вызывающий слушателя». Означает ли это, что метод doGet должен быть синхронизирован? если все методы doGet находятся в разных потоках и только один поток одновременно должен вызывать слушателя? если бы кто-нибудь мог помочь с пониманием, я был бы признателен. спасибо

это пример сервлета:

    @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();
    }
}
}
...