appengine 上线程安全 java 的清单
我的 java appengine 项目不会在请求之间保留任何状态,除了 Memcache 的以下用途之外:
- Objectify 使用 memcache 来缓存数据存储获取
- 我使用 memcache 作为在多个请求后批处理清理任务的方法(例如
if (memcache 不不要认为清理任务已经在运行)安排另一个清理任务
)。
我没有对任何对象的全局/静态引用,除了:
- 当前经过身份验证的用户保存在
static ThreadLocal
对象中。这意味着每个请求都会获得自己的用户副本,对吧? - 我有一个处理所有数据操作的类,并且实例作为静态 DataCoordinator 对象中的一种全局变量保存。
为了使我的代码线程安全,我需要注意什么?由于多个线程可以访问它,我是否需要将 synchronized
关键字放入 DataCoordinator
实现中的每个方法声明中? ThreadLocal
对象是否总是会为每个线程创建一个单独的 User
对象,以便每个请求都单独进行身份验证?
我是线程安全思想的新手。我应该读什么?
感谢您的帮助,并对缺乏具体性表示歉意。
My java appengine project does not keep any state between requests except for the following uses of Memcache:
- Objectify uses memcache to cache datastore gets
- I use memcache as a way to batch cleanup tasks after multiple requests (e.g.
if (the memcache doesn't think a cleanup task is already running) schedule another cleanup task
).
I have no global / static references to any objects, except:
- The current authenticated user is kept in a
static ThreadLocal<User>
object. This means each request will get its own copy of a User, right? - I have a class that handles all data manipulation, and an instance is kept as a sort of global variable in a
static DataCoordinator
object.
What do I need to look out for to make my code thread-safe? Do I need to throw a synchronized
keyword into every method declaration in my DataCoordinator
implementation, since multiple threads could access it? Is it true that the ThreadLocal<User>
object will always make a separate User
object for each thread, so that each request will be authenticated separately?
I'm a total newbie to thread-safe thinking. What should I read?
Thanks for any help, and sorry for the lack of specificity.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您应该注意的第一件事是应用程序引擎可以在多个服务器上复制您的应用程序。这意味着您的静态变量仅在一台服务器上是唯一的。因此,您的
DataCoordinator
将仅协调单个服务器上的数据访问。因此,如果您需要运行应用程序的所有服务器的通用数据,您应该始终使用数据存储(或在某些情况下使用 gae HTTP 会话机制)。关于
DataCoordinator
的线程安全:如果这些方法不是以线程安全的方式实现的,则只需同步该协调器的方法即可。例如,您不需要同步任何不访问任何实例/静态数据而仅从数据存储区获取数据的方法。如果方法访问可变的公共实例/静态数据(也同时写入),则在大多数情况下,您可以在特殊监视器上同步所访问的数据,而不是在整个协调器上同步。关于用于存储身份验证令牌的 ThreadLocal:您可以这样做(例如,我在 gae 中为 GWT 请求工厂请求进行用户身份验证),是的,每个线程都会有自己的变量值,只要当你为该线程设置它时。这意味着最好确保为每个线程设置该变量,并且建议在设置该变量后使用
try
-finally
- 块,最终删除身份验证数据使用后。为什么?否则,可能发生的最糟糕的事情是属于用户 B 请求的线程仍然具有用户 A 的身份验证令牌。这是因为应用程序服务器中使用的线程通常在请求之间汇集,而不是清理和重新创建。我不能对 memcache 说什么,因为我没有使用过它。
一般来说,您必须知道服务器可以同时处理任何 Web 请求(servlet/JSP/...)。因此,这些线程访问的任何可变共享资源都应该同步或以线程安全的方式实现。
也许 http://download.oracle.com/javase/tutorial/essential/concurrency/< /a> 是阅读它的一个很好的起点。
First thing you should be aware of is that app engine can replicate your application on multiple servers. That means your static variables will be unique only on one server. Thus your
DataCoordinator
will coordinate only the data access on a single server. So if you need common data to all servers running your app you should always use the data store for that (or gae HTTP session mechanism in some cases).Regarding thread safety of
DataCoordinator
: You only need to synchronize methods of this coordinator if these methods are not implemented in a thread safe manner. For example you do not need to synchronize any methods that don't access any instance/static data but merely fetch data from the datastore. If methods access common instance/static data that is mutable (is also concurrently written) you can synchronize on a special monitor for the accessed data in most cases instead of synchronizing on the whole coordinator.Regarding your
ThreadLocal
used to store auth token: You can do that (I do that for example for user authentication in gae for GWT request factory requests) and yes, every thread will have it's own variable value as long as you set it for that thread. Meaning it is best to make sure that the variable is set for each thread and it is adviceable to use atry
-finally
-block after setting it that eventually removes the authentication data after use. Why? Worst thing that could happen otherwise would be that a thread that belongs to user B's request still has an auth token of user A. That is because threads used in application server are usually pooled between requests instead of cleand up and recreated.I cannot say anything about memcache since I have not used it.
Generally you have to be aware that any web requests (servlet/JSP/...) can be handled concurrently by the server. So any mutable shared resources that are accessed by those threads should be either synchronized or implemented in a thread safe manner.
Maybe http://download.oracle.com/javase/tutorial/essential/concurrency/ is a good starting point to read into it.
如果您有单例类,则每个创建的虚拟机/实例的代码将仅创建/使用一个实例。
可以从以下位置获取对此单例的引用:
- 由同一实例提供服务的两个连续请求(取决于您的设置它们停留的时间,或者保留实例是否正在运行)
- 如果您将线程安全设置为 true,则两个并行请求在同一实例上的单独线程中运行。
我已经编写并测试了代码来向自己确认这一点,并进行了部署和测试。如果一个请求中的代码使用单例来修改其成员变量之一,那么它会针对并行运行的另一个请求进行修改。
一旦您弄清楚虚拟机的生命周期以及使用多少个线程(仅 1 个或多个)来处理传入请求,所有这些实际上都是合乎逻辑的。
此外,系统变量可以在一个请求中的代码中修改并在另一个请求中读取......这是两个并行请求/线程可以交互的第二种方式。
请参阅我关于 GAE/J 中的多线程的博客文章,了解更多详细信息:
http://devcon5.blogspot.com/2012/09/threadsafe -in-appengine-gaej.html
If you have singleton classes, then only ONE instance will be created/used by your code per VM/Instance created.
A reference to this singleton can be fetched from:
- two sequential requests made one after another that are served by the same instance (depends on your settings how long they stay around, or if a reserved instance is running)
- two parallel requests running in separate threads on the same instance IF you have threadsafe set to true.
I have written and tested code to confirm this to myself, and deployed and tested. If code in one requests uses the singleton to modify one of its member variables, then it is modified for the other request running in parallel.
All logical really, once you figure out the lifetime of a VM and how many threads (only 1, or many) are used to serve incoming requests.
Also, System variables can be modified in code in one request and read in another...a second way two parallel requests/threads can interact.
See more details on this in my blog post on multi-threading in GAE/J here:
http://devcon5.blogspot.com/2012/09/threadsafe-in-appengine-gaej.html