Контрольный список для потокобезопасного Java на appengine - PullRequest
6 голосов
/ 31 октября 2011

Мой проект Java Appengine не сохраняет состояния между запросами, за исключением следующих случаев использования Memcache:

  • Objectify использует memcache для кэширования хранилища данных получает
  • Я использую memcache как способдля пакетной очистки задач после нескольких запросов (например, if (the memcache doesn't think a cleanup task is already running) schedule another cleanup task).

У меня нет глобальных / статических ссылок на какие-либо объекты, кроме:

  • Текущий аутентифицированный пользовательхранится в static ThreadLocal<User> объекте.Это означает, что каждый запрос получит свою собственную копию User, верно?
  • У меня есть класс, который обрабатывает все манипуляции с данными, и экземпляр сохраняется как своего рода глобальная переменная в объекте static DataCoordinator.

На что мне нужно обращать внимание, чтобы сделать мой код поточно-ориентированным?Нужно ли добавлять ключевое слово synchronized в каждое объявление метода в моей реализации DataCoordinator, так как к нему могут обращаться несколько потоков?Правда ли, что объект ThreadLocal<User> всегда будет создавать отдельный объект User для каждого потока, так что каждый запрос будет проходить аутентификацию отдельно?

Я абсолютный новичок в многопотоковом мышлении.Что я должен прочитать?

Спасибо за любую помощь, и извините за отсутствие конкретики.

Ответы [ 2 ]

7 голосов
/ 31 октября 2011

Первое, что вам следует знать, это то, что механизм приложений может реплицировать ваше приложение на нескольких серверах. Это означает, что ваши статические переменные будут уникальными только на одном сервере. Таким образом, ваш DataCoordinator будет координировать только доступ к данным на одном сервере. Поэтому, если вам нужны общие данные для всех серверов, на которых работает ваше приложение, вы всегда должны использовать хранилище данных для этого (или механизм сеанса gae HTTP в некоторых случаях).

Относительно безопасности потока DataCoordinator: Вам нужно синхронизировать методы этого координатора, только если эти методы не реализованы в поточно-ориентированном режиме. Например, вам не нужно синхронизировать какие-либо методы, которые не обращаются к каким-либо экземплярам / статическим данным, а просто извлекают данные из хранилища данных. Если методы обращаются к обычным экземплярам / статическим данным, которые являются изменяемыми (также записываются одновременно), вы можете синхронизировать на специальном мониторе для данных, к которым осуществляется доступ, в большинстве случаев вместо синхронизации по всему координатору.

Относительно вашего ThreadLocal, используемого для хранения токена аутентификации: вы можете сделать это (я делаю это, например, для аутентификации пользователя в gae для запросов фабрики запросов GWT) и да, каждый поток будет иметь свое собственное значение переменной, пока вы установите его для этой темы. Это означает, что лучше всего убедиться, что переменная установлена ​​для каждого потока, и после установки рекомендуется использовать try - finally -блок, который в конечном итоге удаляет данные аутентификации после использования. Зачем? В противном случае может случиться так, что поток, принадлежащий запросу пользователя B, по-прежнему имеет токен аутентификации пользователя A. Это связано с тем, что потоки, используемые на сервере приложений, обычно объединяются между запросами, а не очищаются и воссоздаются.

Я не могу ничего сказать о memcache, так как я не использовал его.

Как правило, вы должны знать, что любые веб-запросы (сервлет / JSP / ...) могут обрабатываться сервером одновременно. Поэтому любые изменяемые общие ресурсы, к которым получают доступ эти потоки, должны быть либо синхронизированы, либо реализованы безопасным для потоков образом.

Может быть, http://download.oracle.com/javase/tutorial/essential/concurrency/ - хорошая отправная точка для прочтения.

3 голосов
/ 20 сентября 2012

Если у вас есть одноэлементные классы, то только один экземпляр будет создан / использован вашим кодом для каждой созданной виртуальной машины / экземпляра.

Ссылка на этот синглтон может быть получена из:
- два последовательных запроса, выполняемых один за другим, которые обслуживаются одним и тем же экземпляром (зависит от ваших настроек, как долго они остаются, или если запущен зарезервированный экземпляр)
- два параллельных запроса, выполняющиеся в отдельных потоках в одном экземпляре, если у вас установлен потокобезопасный тип true.

Я написал и протестировал код, чтобы подтвердить это сам, а также развернул и протестировал. Если код в одном запросе использует синглтон для изменения одной из его переменных-членов, то он изменяется для другого запроса, выполняемого параллельно.

На самом деле все логично, как только вы выясните время жизни ВМ и сколько потоков (только 1 или много) используются для обслуживания входящих запросов.

Кроме того, системные переменные можно изменять в коде в одном запросе и читать в другом ... вторым способом могут взаимодействовать два параллельных запроса / потока.

Подробнее об этом см. В моем блоге о многопоточности в GAE / J здесь:
http://devcon5.blogspot.com/2012/09/threadsafe-in-appengine-gaej.html

...