1. 普通
简单的懒汉模式如下:
public class Lazy {
private static Lazy lazy;
public static Lazy getInstance(){
if(lazy == null){
lazy = new Lazy();
}
return lazy;
}
}
懒汉方式进行初始化的步骤就是在Jvm进行类加载的时候不进行对象创建,直到调用者调用getInstance()的时候才返回一个新的实例。
这样做的好处很显然,刚才也有提到,JVM会进行延迟加载,不至于在初始化的时候完成很多操作。
但是又有一个很显然的问题诞生了,那就是线程不安全。
考虑下面这样一个情景,当线程A与B同时访问getInstance()方法,假设A先到达,判断instance为null后准备创建对象,B也正好检查完准备创建,那么意想不到的事情就来了,A,B创建了两个不同的对象,而不是我们目标意义上的单例,由于这种线程执行的不确定性,导致这种写法是线程不安全的。
2. 优化
既然线程不安全,那么试试synchronized关键字。
public class Lazy {
private static Lazy lazy;
public synchronized static Lazy getInstance(){
if(lazy == null){
lazy = new Lazy();
}
return lazy;
}
}
加上同步关键字之后,线程变得安全,但是又随之而来一个问题,就是效率问题,我们知道同步的含义,同步是一种排队等待的执行方式,所以排队等待会浪费很多的时间,在多次调用中,并不能发挥很优的效率。
3.再优化
public class Lazy {
private static Lazy lazy;
public static Lazy getInstance(){
if(lazy == null){
synchronized (Lazy.class) {
if(lazy == null)
lazy = new Lazy();
}
}
return lazy;
}
}
使用同步关键字的效率低下原因在于,每个线程都要等待判断对象是否为空,但是实际上一次创建之后,不出意外后面的都是不为null的,白白浪费了很多等待时间,于是终极优化采用了同步语句块来实现同步。
通过对Lazy.class 对象加锁,来实现创建对象的同步,所以判断是否为空的过程就省去了。
但是当一群线程蜂拥而至的时候,判断lazy引用为null,就要争先恐后的去创建对象,这样当然不行,所以只能有一个精子(线程)进入卵细胞(临界区)【莫名其妙的开车】,当它进入临界区创建了一个对象后,其他的线程再进去就要判断lazy是否已经被创建。
最终就得到了一个 线程安全,延迟加载,效率又高的懒汉模式。
网友评论