饿汉式单例模式:
public class Hungry { private Hungry() {} private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance() { return HUNGRY; } }
懒汉式单例:
public class lazyMan { private lazyMan() {} private static lazyMan LAZY; public static lazyMan getInstance() { if(LAZY == null) { //1:A线程执行 LAZY = new lazyMan(); //2:B线程执行 } return LAZY; } }
懒汉式单例模式的问题
首先声明一点——代码2这一行可以分解成:
- 分配对象的内存空间初始化对象设置LAZY指向刚分配的内存地址
上边是我们任务的顺序,但是编译器可能对这一行代码优化成:
- 分配对象的内存空间设置LAZY指向刚分配的内存地址初始化对象
假设A线程执行代码1的同时,B线程执行代码2,此时线程A可能会看到LAZY引用的对象还没有完成初始化,这肯定会出错;或者另一种情况,线程A和B同时执行到代码1,最终同时进入代码块new对象,返回了两个不同的对象;
解决方案
我们可以对getInstance()方法进行同步处理来解决这种问题:
public class lazyMan { private lazyMan() {} private static lazyMan LAZY; public synchronized static lazyMan getInstance() { if(LAZY == null) { LAZY = new lazyMan(); } return LAZY; } }
那么问题来了,我们都知道synchronized 是一个重量级锁,如果getInstance()方法被多个线程频繁调用,将会导致程序执行性能的下降。
解决方案(双重检查锁)
public class lazyMan { private lazyMan() {} private volatile static lazyMan LAZY; //1 public static lazyMan getInstance() { if(LAZY == null) { //2 synchronized (lazyMan.class) { if(LAZY == null) LAZY = new lazyMan(); } } return LAZY; } }
如上面的代码:
- 多个线程同一时间创建对象时,会通过加锁保证只有一个线程创建锁;如果检查LAZY不为空那么就不需要执行下面的加锁和初始化 *** 作,可以大幅的减少synchronized带来的性能开销。代码1处使用volatile
修饰也很有必要,因为synchronized并不能禁止指令重排,使用volatile才能保证多线程情况下线程们看到的LAZY对象时一个初始化完成后的
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)