Я пытаюсь кэшировать значения в ConcurrentHashMap
в Session
. Чтобы избежать условий гонки и убедиться, что моя карта создана до того, как какие-либо потоки попытаются ее использовать, я использую HttpSessionListener.sessionCreated()
, чтобы добавить карту в Session
:
@Override
public void sessionCreated(HttpSessionEvent event) {
event.getSession()
.setAttribute(MY_CACHE_KEY, new ConcurrentHashMap());
}
Гарантируется ли выполнение этого кода до того, как другие потоки получат доступ к сеансу (например, через request.getSession()
)?
Я посмотрел на HttpSessionListener
JavaDoc и Servlet 4.0 Spec. и, похоже, нет никаких гарантий безопасности потоков.
Серлвет Спец. ссылается на безопасность потока сеанса несколько раз, но ни одна из этих ссылок не относится к слушателям сеанса и созданию сеанса, как я их понимаю:
7.7.1 Проблемы с потоками
Несколько сервлетов, выполняющих потоки запросов, могут иметь активный доступ к одному и тому же объекту сеанса в одно и то же время. Контейнер должен обеспечивать манипулирование внутренними структурами данных, представляющими атрибуты сеанса.
выполняется потокобезопасным способом. Разработчик несет ответственность за потокобезопасный доступ к самим объектам атрибута. Это защитит коллекцию атрибутов внутри объекта HttpSession от одновременного доступа,
исключение возможности для приложения вызывать повреждение этой коллекции. Если явно не указано иное в спецификации (например, в разделе 7.7.1, «Проблемы с многопоточностью» на стр. 7-67 для объектов сеанса), объекты, полученные из запроса или ответа, должны считаться не поточно-безопасными. Это включает, но не ограничивается, PrintWriter, возвращенный из
ServletResponse.getWriter () и OutputStream возвращаются из ServletResponse.getOutputStream ().
11.5 Экземпляры и потоки слушателя
Контейнер необходим для завершения создания экземпляров классов слушателей в веб-приложении до начала выполнения первого запроса в приложении. Контейнер должен поддерживать ссылку на каждый экземпляр прослушивателя до тех пор, пока последний запрос не будет обработан для веб-приложения.
Изменения атрибутов в объектах ServletContext и HttpSession могут происходить одновременно. Контейнеру не требуется синхронизировать полученные уведомления с атрибутивными классами слушателей. Классы слушателей, которые поддерживают состояние, несут ответственность за целостность данных и должны явно обрабатывать этот случай.
Кажется очевидным, что sessionCreated()
должно завершиться до того, как потоки получат доступ к сеансу, но "очевидно правильный код" был небезопасен для многопоточности до .
Эта неоднозначность не существует для ServletContextLister.contextInitialized()
, поскольку она гарантированно завершится до инициализации Servlet
, а Servlet.init()
гарантированно будет однопоточной и будет выполняться перед любыми запросами .
По крайней мере, я протестировал Tomcat, и он гарантирует, что sessionCreated()
завершится до того, как request.getSession()
вернется. Я проверил, установив точку останова в sessionCreated()
и отправив запрос с именем request.getSession()
. Этот запрос не был выполнен, пока я не продолжил с точки останова. Однако поведение одной реализации контейнера Servlet
не является убедительным доказательством того, что все контейнеры / серверы ведут себя таким образом.