Double-checked locking and the Singleton pattern

1.什么是单例模式

单例模式的想法是保证只有一个Singleton object能创建,私有的构造器和公开的getInstance()方法保证只能创建一个对象。这种方式在单线程环境下是可以走通的,但是在多线程环境下,必须要保证getInstance()这个方法是synchronization才行。

package double2.check.locking.singleton;


public class Singleton {

private boolean inUse;

private static Singleton instance;

private Singleton() {
inUse = true;
}

public static Singleton getInstance() {
if (instance == null)   //1
instance = new Singleton(); //2
return instance;       //3
}


}

考虑如下情况:

1.thread 1 调用getInstance()方法,发现instance还是null

2.thread 1进入if后,想要执行2的时候被thread2打断,thread 1阻塞

3.thread 2调用getInstance()方法,发现instance还是null

4.thread 2进入if块中,创建了 Singleton 的对象 o1,并且返回了o1

5.thread 1打断thread 2,创建 Singleton的对象 o2,并且返回了o2

本来是想创建一个Singleton对象的,现在却创建了两个Singleton对象。

如果要对这个问题做修正的话,可以在getInstance()方法上加上synchronized关键字,使得某个时间段内,只有一个线程才能进入getInstance()方法。

public static synchronized Singleton  getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}

但是,这样做并不好。因为每个thread去调用都要去获取锁,我们知道获取锁是要资源的;并且会影响并发性,因为每次只能有一个线程才能执行getInstance()这个方法。实际上我们只要在第一次调用这个方法获取锁就行了。

2.用synchronized 来修正

public static  Singleton  getInstance() {
if (instance == null)  //1
synchronized(Singleton.class) { //2
instance = new Singleton();
}
return instance;
}

这样做也不行,我们得要的结果跟第一个版本是一样的。当thread 1进入if块,发现instance是null,然后进入2块试图创建Singleton对象。这时候thread 1被thread2打断,thread 2 进入if块,发现instance还是null,然后进入synchronized块,创建Singleton对象,并且返回;这时候thread 2被thread 1打断,thread 1创建对象,返回。最后发现创建了两个Singleton对象。

3.Double-checked locking

public static  Singleton  getInstance() {
if (instance == null)//1
synchronized(Singleton.class) {//2
if ( instance == null )//3
instance = new Singleton();//4
}
return instance;
}

thread 1,thread 2同时进入1后,考虑如下情况

1.thread 1 进入2获取Singleton锁后,执行到4

2.thread 1 被thread 2打断,thread 2开始执行

3.thread 2执行到2,发现需要获取Singleton的锁,thread 2阻塞

4.thread 1执行,创建Singleton对象,并且返回

5.thread 1被thread 2打断

6.thread 2开始执行2,这次能成功获取Singleton的锁

7.thread 2执行3发现instance现在不是空的,就马上返回了,不会再去执行4.

这次真正的只创建了一个对象。但是在现在的jvm上不能保证正确,因为新的jMM支持out-of-order-write乱序写。

4.out-of-order-write

因为有乱序写的存在,在线程1在执行4构建Singleton 对象的时候,instance可能就已经是非null了。例如:

1.thread1进入4,设置instance为非null,这时候Singleton 并没有真正的创建好。

2.thread2调用getInstance()方法,发现instance非null,之间返回Singleton 对象

3.thread1完成Singleton 的创建,并且返回。

结果thread2得到了一个并没有构建好的Singleton 对象。

5.解决方式,不用懒加载模式,直接用饥渴模式

package double2.check.locking.singleton;


public class Singleton {

private boolean inUse;

private static Singleton instance = new Singleton();

private Singleton() {
inUse = true;
}

public static  Singleton  getInstance() {
return instance;
}


}

或者用synchronized 方法。

public static synchronized Singleton  getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值