1.通常懒加载初始化的典型实现方式:
public class LazyInit {
public static Resource resource;
public static Resource getResource() {
if (resource == null) {
resource = new Resource();
}
return resource;
}
}
在单线程应用程序中,以上示例保证按预期工作。但在多线程应用程序可能有各种不同的结果。它可以按预期工作,它也可能多次初始化资源,它也可能发布部分对象(读取部分对象), 所以为不安全实现方式。
2. 安全的实现方式
public class LazyInit {
public static Resource resource;
public synchronized static Resource getResource() {
if (resource == null) {
resource = new Resource();
}
return resource;
}
}
现在,resource由LazyInit类保护。只有一个线程可以进入此方法并检查resource是否已初始化,如果是,则返回当前实例。
3. 在某些情况下,您可能为了加快访问Resource实例的过程采取双重检查
public class LazyInit {
public static Resource resource;
public static Resource getResource() {
if (resource == null) {
synchronized (LazyInit.class){
if (resource == null){
resource = new Resource();
}
}
}
return resource;
}
}
如果已异步初始化,则检查Resource ,否则您将同步初始化它。
看起来很好,但它不如(2.安全的实现方式)。在这种情况下,一个线程可以看到Resource部分加载,这意味着它与null不同,但它却还没有完全构造。
JSR-133表示,Java保证最终字段在构造完成之前“冻结”。我们来看看Resource文件。
public class LazyInit {
public final int FINAL_VALUE;
public int value;
public Resource(){
FINAL_VALUE = 1;
value = 1;
}
}
从LazyInit.getResource()方法检索Resource的一个线程实际上会获得部分对象:
...
int aFinalValue = LazyInit.getResource()。FINAL_VALUE //保证1
int aValue = LazyInit.getResource()。value //可以是0
...
用于双重检查锁定的解决方案需要易失性类型的资源。建议不要将此解决方案用于静态字段.
public class LazyInit {
private volatile Resource resource;
public Resource getResource() {
Resource result = resource;
if (result == null) {
synchronized (this){
result = resource;
if (result == null){
resource = new Resource();
}
}
}
return resource;
}
}
易失性保证字段在所有线程中返回最新值并在写入时锁定它。请注意,我们将volatile值分配给普通变量,因此在检查时不知道它就不会改变, 所以将它赋值给一个临时变量。
4. 最快速初始化方式
public class EagerInit {
private static Resource resource = new Resource();
public static Resource getResource() {
return resource;
}
}
最终字段不仅承诺在构造完成之前被“冻结”,而且静态字段也必须在构造函数触发之前初始化。
该示例保证在使用resource之前实例化Resource.
上面的示例是线程安全的,但它是由JVM立即初始化的,并不是我们想要的。我们可以使用这些知识,但这并不是我们想要的。
5. Bill Pugh Singleton(单列模式)
事实证明,JVM对内部静态类的处理方式不同。它们保持隐藏,直到实际使用时才初始化实例对象。这个小信息驱使我们进入线程安全单例初始化的下一个例子
public class LazyInit {
private static class Holder{
public static Resource resource = new Resource();
}
public static Resource getResource(){
return Holder.resource;
}
}
这是Java中最推荐的延迟初始化方法。超级简单,超级安全。