工作生活

单例模式与线程安全

2019-07-04  本文已影响0人  Mixqum

转载
http://www.barryzhang.com/archives/521
https://www.jianshu.com/p/0442fb1e0c7c

  1. double check + volatile单例,保证线程安全
    double check : 指两次if (instance == null)的判断:
  // 不是原子操作
  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;
    }
}
  1. 懒汉式单例 + 静态内部类 保证线程安全
    在第一次使用时,实例化对象
/**
 * 演示 懒汉式单例 + 静态内部类
 */
public class ToastUtil {

    private ToastUtil(){

    }

    static class  ToastHolder{
        private static ToastUtil instance = new ToastUtil();
    }

    public static ToastUtil getInstance(){
        return ToastHolder.instance;
    }
}

上一篇 下一篇

猜你喜欢

热点阅读