懒汉式单例模式可能存在线程安全问题
下文笔者讲述创建单例模式时--线程安全的注意事项分享,如下所示
一.在方法上加上synchronized锁的单例模式
public class SingleteExample {
private static SingleteExample singleteExample;
private SingleteExample() {
}
public synchronized static SingleteExample getInstance() {
if (singleteExample == null) {
singleteExample = new SingleteExample();
}
return singleteExample;
}
}
此方式创建的单例模式
可保证线程安全
但性能会非常差
在并发情况下,其他线程会被阻塞
二.在代码块上使用synchronized加锁
public class SingleteExample2 {
private static SingleteExample2 singleteExample;
private SingleteExample2() {
}
public static SingleteExample2 getInstance() {
if (singleteExample == null) {
synchronized (SingleteExample2.class) {
singleteExample = new SingleteExample2();
}
}
return singleteExample;
}
}
这种方法只有在SingleteExample2对象为空时才会创建对象
锁的力度更细
但在并发情况下
线程A,B同时执行这个方法
同时进行判断,都为空,A线程得到锁,初始化,
B线程等A线程释放锁后
B线程接着进行初始化
A B两个线程得到的不是同一个对象。
三.双重检查
public class SingleteExample3 {
private static SingleteExample3 singleteExample;
private SingleteExample3() {
}
public static SingleteExample3 getInstance() {
if (singleteExample == null) {
synchronized (SingleteExample3.class) {
if (singleteExample == null) {
singleteExample = new SingleteExample3();
}
}
}
return singleteExample;
}
}
B线程得到锁后初始化前仍会判断SingleteExample3对象是否为空
这时SingleteExample3对象已经初始化了,判断结果为false
B不会再次创建对象
四.指令重排序
创建一个对象分为三步
1.分配内存空间
2.初始化对象
3.将内存空间的地址赋值给对象的引用。
其中2、3步执行时虚拟机是会重排序的
若A线程执行时JVM将2 3步重排序
那么此时对象的引用指向的内存空间仅仅只是一个地址,
这时候B线程进行第一次为空判断时,发现不为空,会将对象的引用返回
使用volatile关键字可禁止JVM重排序
例
public class SingleteExample4 {
private static volatile SingleteExample4 singleteExample;
private SingleteExample4() {
}
public static SingleteExample4 getInstance() {
if (singleteExample == null) {
synchronized (SingleteExample4.class) {
if (singleteExample == null) {
singleteExample = new SingleteExample4();
}
}
}
return singleteExample;
}
}
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


