转载
http://www.barryzhang.com/archives/521
https://www.jianshu.com/p/0442fb1e0c7c
- double check + volatile单例,保证线程安全
double check : 指两次if (instance == null)
的判断:
- 外层
if(instance==null)
检查,避免每次获取对象锁 - 内层
if(instance==null)
检查,锁住对象,避免多线程的时候,对象被创建多个
原子操作(atomic) : 就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作
声明并赋值就不是一个原子操作
// 不是原子操作
int i = 1;
指令重排 :计算机为了提高执行效率,会做的一些优化,在不影响最终结果的情况下,可能会对一些语句的执行顺序进行调整
volatile:其作用是禁止指令重排。因为对象创建的时候在JVM经历3个步骤:
1.分配内存
2.调用构造函数,初始化成员变量,创建实例
3.将实例指向内存
如果不加volatile,在多线程情况下,1-2-3并不会按顺序执行,比如出现实例非null(执行了1,2),另外一个线程进入第一层check时,拿到的是非空实例,但没有被分配内存(没执行3),导致出现错误(也就是没有被完全初始化就被其他线程使用了)。
/**
* 演示 double check + volatile单例,保证线程安全
*/
public class StringUtil {
// 在多线程环境中,volatile能保证共享变量的可见性以及一定程度的有序性
// volatile关键字的一个作用是禁止指令重排,把instance声明为volatile之后,
// 对它的写操作就会有一个内存屏障,这样,在它的赋值完成之前,就不用会调用读操作。
// volatile 阻止的不是singleton = new Singleton()这句话内部[1-2-3]的指令重排,
// 而是保证了在一个写操作([1-2-3])完成之前,不会调用读操作(if (instance == null))
private volatile static StringUtil instance;
private StringUtil(){
}
public static StringUtil getInstance(){
// 先判断是否存在。避免每次获取锁,开销较大
if(instance==null){
synchronized (StringUtil.class){
// 防止可能出现多个实例的情况
if(instance==null){
instance = new StringUtil();
}
}
}
return instance;
}
}
- 懒汉式单例 + 静态内部类 保证线程安全
在第一次使用时,实例化对象
/**
* 演示 懒汉式单例 + 静态内部类
*/
public class ToastUtil {
private ToastUtil(){
}
static class ToastHolder{
private static ToastUtil instance = new ToastUtil();
}
public static ToastUtil getInstance(){
return ToastHolder.instance;
}
}
网友评论