创建线程安全的单例模式
程序员文章站
2022-07-14 09:17:35
...
1、饿汉模式–类加载就实例化–线程安全
package SingleInstance;
/**
* 饿汉式单例模式:实例化类就加载实例-天生线程安全
*/
public class SingleInstance {
//私有化构造方法
private SingleInstance(){}
//创建实例
private static SingleInstance instance = new SingleInstance();
//提供实例获得方法
public static SingleInstance getInstance(){
return instance;
}
}
2、懒汉式单例–需要的时候在实例化–非线程安全
package SingleInstance;
/**调用get方法的时候判断实例对象是否为空,为空在实例化,有线程安全问题。
*/
public class SingleInstance_lazy {
private SingleInstance_lazy(){}
private static SingleInstance_lazy singleInstance_lazy = null;
public static SingleInstance_lazy getSingleInstance_lazy(){
if (singleInstance_lazy==null){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleInstance_lazy = new SingleInstance_lazy();
}
return singleInstance_lazy;
}
}
调用方法:
public static void lazysingletest(){
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
public void run() {
SingleInstance_lazy singleInstance_lazy = SingleInstance_lazy.getSingleInstance_lazy();
System.out.println(singleInstance_lazy);
}
}).start();
}
}
输出:
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
加上synchronized 关键字
public class SingleInstance_lazy {
private SingleInstance_lazy(){}
private static SingleInstance_lazy singleInstance_lazy = null;
public static synchronized SingleInstance_lazy getSingleInstance_lazy(){
if (singleInstance_lazy==null){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleInstance_lazy = new SingleInstance_lazy();
}
return singleInstance_lazy;
}
}
但是会有效率问题,线程会一直自旋等待。锁的粒度太大,我们考虑减少锁的代码块
public class SingleInstance_lazy {
private SingleInstance_lazy(){}
private static SingleInstance_lazy singleInstance_lazy = null;
public static SingleInstance_lazy getSingleInstance_lazy() {
if (singleInstance_lazy==null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (SingleInstance_lazy.class){
singleInstance_lazy = new SingleInstance_lazy();
}
}
return singleInstance_lazy;
}
}
但是又会出现线程安全性问题,假如两个线程同时判断为null
输出:
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
也没有实现单例的效果
所以考虑优化成双重判断:
public class SingleInstance_lazy {
private SingleInstance_lazy(){}
private static SingleInstance_lazy singleInstance_lazy = null;
public static SingleInstance_lazy getSingleInstance_lazy() {
if (singleInstance_lazy==null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (SingleInstance_lazy.class){
if (singleInstance_lazy==null)
singleInstance_lazy = new SingleInstance_lazy();
}
}
return singleInstance_lazy;
}
}
但是这样也不能保证百分百不存在安全性问题。
指令重排序可能导致代码执行顺序并不是按照上面代码的顺序。
在new操作的时候,
1、分配内存;
2、内存初始化单例SingleInstance_lazy对象;
3、内存地址赋值给SingleInstance_lazy变量;
优化过后:
1、分配内存;
2、内存地址赋值给SingleInstance_lazy变量;
3、内存初始化单例SingleInstance_lazy对象;
那么在线程1在执行getSingleInstance_lazy方法的时候,执行new到的过程到内存地址分配到SingleInstance_lazy变量,突然切换线程2,线程2在第一个判空过程就会得到SingleInstance_lazy!=null,然而只是一个空指针。
所以加上关键字volatile,保证不让编译器不优化顺序
private static volatile SingleInstance_lazy singleInstance_lazy;
上一篇: Android设计模式之单例模式