除了Memcache的以下用法之外,我的java appengine项目不会在请求之间保持任何状态:
if (the memcache doesn't think a cleanup task is already running) schedule another cleanup task
)。我没有对任何对象的全局/静态引用,除了:
static ThreadLocal<User>
对象中。这意味着每个请求都将获得自己的用户副本,对吗?static DataCoordinator
对象中的一种全局变量保存。我需要注意什么才能使我的代码线程安全?我是否需要在synchronized
实现中的每个方法声明中抛出DataCoordinator
关键字,因为多个线程可以访问它?是ThreadLocal<User>
对象是否总是为每个线程创建一个单独的User
对象,以便每个请求将单独进行身份验证?
我是线程安全思维的新手。我该怎么读?
感谢您的帮助,并对缺乏特异性感到抱歉。
答案 0 :(得分:7)
您应该注意的第一件事是应用引擎可以在多台服务器上复制您的应用程序。这意味着您的静态变量仅在一台服务器上是唯一的。因此,您的DataCoordinator
将仅协调单个服务器上的数据访问。因此,如果您需要为运行应用程序的所有服务器提供公共数据,则应始终使用数据存储(或在某些情况下使用gae HTTP会话机制)。
关于DataCoordinator
的线程安全性:如果这些方法没有以线程安全的方式实现,则只需要同步此协调器的方法。例如,您不需要同步任何不访问任何实例/静态数据但仅从数据存储区获取数据的方法。如果方法访问可变的公共实例/静态数据(也是同时写入的),则在大多数情况下,您可以在特殊监视器上同步访问的数据,而不是在整个协调器上进行同步。
关于用于存储身份验证令牌的ThreadLocal
:您可以这样做(我这样做是为了在GWT请求工厂请求的gae中进行用户身份验证),是的,每个线程都会拥有自己的变量值当你为那个线程设置它时。这意味着最好确保为每个线程设置变量,并且建议在设置它之后使用try
- finally
- 块,最终在使用后删除身份验证数据。为什么?否则可能发生的最糟糕的事情是属于用户B的请求的线程仍然具有用户A的身份验证令牌。这是因为应用程序服务器中使用的线程通常在请求之间汇集而不是清理并重新创建。
由于我没有使用它,我无法对memcache说些什么。
通常,您必须知道服务器可以同时处理任何Web请求(servlet / JSP / ...)。因此,这些线程访问的任何可变共享资源都应该以线程安全的方式同步或实现。
也许http://download.oracle.com/javase/tutorial/essential/concurrency/是阅读它的好起点。
答案 1 :(得分:3)
如果您有单例类,那么每个VM /实例创建的代码只会创建/使用一个实例。
可以从以下内容获取对此单例的引用:
- 由同一个实例提供的两个连续顺序请求(取决于您的设置它们保持多长时间,或者预留实例是否正在运行)
- 如果您将threadsafe设置为true,则在同一实例上的不同线程中运行两个并行请求。
我已经编写并测试了代码以自行确认,并进行部署和测试。如果一个请求中的代码使用单例修改其成员变量之一,则会针对另一个并行运行的请求对其进行修改。
一旦你弄清楚虚拟机的生命周期以及使用多少个线程(只有1个或多个)来传递传入请求,所有这些都是合乎逻辑的。
此外,系统变量可以在一个请求中的代码中修改,并在另一个请求中读取...第二种方式是两个并行请求/线程可以交互。
在我的GAE / J多线程博客文章中查看更多详细信息:
http://devcon5.blogspot.com/2012/09/threadsafe-in-appengine-gaej.html