我有一个Web应用程序,我懒惰地加载组件。有很多Java - 线程同步在Web应用程序
static Bla bla;
...
if(bla == null)
bla = new Bla();
遍布整个代码。我需要做些什么来确保这是线程安全的?我是否应该随时换行,在区块中执行其中一种初始化?这样做有什么问题吗?
我有一个Web应用程序,我懒惰地加载组件。有很多Java - 线程同步在Web应用程序
static Bla bla;
...
if(bla == null)
bla = new Bla();
遍布整个代码。我需要做些什么来确保这是线程安全的?我是否应该随时换行,在区块中执行其中一种初始化?这样做有什么问题吗?
惰性加载静态字段的最佳解决方案,如Effective Java [第二版,第71项, 283]和Java Concurrency in Practice [p。348],是Initialization on demand holder idiom:
public class Something {
private Something() {
}
private static class LazyHolder {
private static final Something something = new Something();
}
public static Something getInstance() {
return LazyHolder.something;
}
}
假设你使用的是Java 1.5或更高版本,你可以这样做:
private static volatile Helper helper = null;
public static Helper getHelper() {
if (helper == null) {
synchronized(Helper.class) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
这保证是线程安全的。
我建议你阅读这篇理解为什么VAR必须是挥发性的,和实际需要对空的双重检查:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
懒惰的实例是唯一真正的问题的一部分。访问这些字段呢?
通常在J2EE应用程序中,您尽可能避免执行此类事情,以便您可以将代码与任何线程问题隔离开来。
也许如果你扩展一个你想要保持的什么样的全局状态有更好的方法来解决这个问题。
这就是说,要直接回答您的问题,您需要确保对这些字段的访问完成同步,读取和写入。在某些情况下,Java 5比使用synchronized更好。我建议您阅读Java Concurrency in Practice以了解这些问题。
最好的办法确实是一个synchronized块包围的一切,并声明变量波动,如:
private static volatile Bla bla;
synchronized{
if(bla == null) bla = new Bla();
}
如果你真的需要有只有一个单一实例分配给bla
随时您的Web应用程序正在运行,您必须记住,应用于变量声明的static
关键字只能确保每个类加载器将有一个读取定义它的类的类定义。
由于BLA是静态的,它可以从包含类 和代码的不同实例访问像 synchronized{...}
或synchronized(this){...}
不抵御此。您必须在所有情况下获得相同对象的锁定,例如synchronized(bla){...}
使用volatile变量很棘手。
它的描述如下: http://www.ibm.com/developerworks/java/library/j-dcl.html
例从上面的链接引用:
class Singleton
{
private Vector v;
private boolean inUse;
private static Singleton instance = new Singleton();
private Singleton()
{
v = new Vector();
inUse = true;
//...
}
public static Singleton getInstance()
{
return instance;
}
}
将工作100%,并且更加清晰(阅读和理解)比较,以双重检查和其他方法。
我会问你为什么认为有必要懒洋洋地加载这些。如果它是一个Web应用程序,并且您知道您需要这些对象,那么为什么您不希望在应用程序启动后加载它们?
请解释延迟加载提供的好处。如果它是静态的,是否有可能不会初始化这些对象?如果答案是否定的,我会挑战设计,并建议您急切地加载启动。
嗨duffymo,我懒洋洋地加载他们的原因是因为我的应用程序在Google App Engine(GAE)上。 GAE根据您网站的负载启动和停止应用程序的实例,并且在用户请求页面时,他们必须等待应用程序中的所有初始化完成才能获得响应。所以我做的是:如果请求发生在尚未完全初始化的实例上,那么我只是将该页面从缓存中提取出来,而不加载某些对象。 – Kyle 2010-02-09 22:39:03
缓存是你的懒惰加载对象之一吗?这不是GAE的一部分吗?我明白了你的观点,谢谢,但是我想知道init进程的长度以及在发生时有人会请求页面的机会。 Singleton购买你的所有东西都可能是你的用户在发出请求时不需要初始化的所有对象。如果你不能保证,这个计划不能帮助。 – duffymo 2010-02-09 23:33:37
嗨Duffymo,缓存不会被延迟加载,因为无论发生什么,我都会在每次请求时都需要它。整个init进程大约需要10秒钟,很可能其他用户会在init进程中间发出请求。我的目标是在init进程完成之前继续使用缓存。一些延迟加载的东西的例子,如果我从缓存中提供的话,这些东西就不需要了:JDO需要大约5秒的时间才能加载,而Spring Framework需要大约2秒的时间。通过延迟加载这些,用户只需要等待约3秒而不是约10秒。 – Kyle 2010-02-10 21:39:10
您还必须100%确定您的代码是按照JDK5合规性级别进行编译的,否则可能会丢弃易失性部分,并且双重检查的锁定将会被设计中断。 – Romain 2010-02-09 22:04:40
谢谢,还有好文章。我试图加载的所有东西都是单例,所以我认为我会去''HelperSingleton {static Helper singleton = new Helper(); }文章中描述的模式。 – Kyle 2010-02-09 22:14:44
这被称为“Double-Checked Locking”,不会做你想做的事。例如见http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html Ooops没有注意到'volatile'。仅当包含类的单个实例时才能在1.5及更高版本中工作。 – 2010-02-09 22:17:23