zoukankan      html  css  js  c++  java
  • DCL-单例模式的线程安全

    DCL-Double Check Lock

    双端检锁机制

    传统单机环境下的单例模式

    public class Test002 {
    
        private static Test002 instance = null;
    
        private Test002(){
            System.out.println(Thread.currentThread().getName() + "	这是一个构造器" );
        }
    
        public static Test002 getInstance(){
            if(instance == null){
                instance = new Test002();
            }
    
            return instance;
        }
    
        public static void main(String[] args) {
            System.out.println(Test002.getInstance() == Test002.getInstance());
            System.out.println(Test002.getInstance() == Test002.getInstance());
            System.out.println(Test002.getInstance() == Test002.getInstance());
        }
    }
    

    输出结果

    多线程环境下

    public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                  Test002.getInstance();
                },String.valueOf(i)).start();
            }
        }
    

    输出结果

    解决方法

    传统可以加sync,但是sync是重锁,存在很大的弊端,直接把整个方法getInstance()加锁了
    所以我们采用DCL-双端检锁机制,只给代码块加sync

    public static Test002 getInstance(){
    
            if(instance == null){
                synchronized(Test002.class){
                    if(instance == null){
                        instance = new Test002();
                    }
                }
            }
            return instance;
        }
    

    但是这样会出现指令重排,高并发多线程环境下,底层会对指令进行优化,导致顺序发生改变,可能会出现问题
    原因是某一个线程执行到第一次检测时,读取到的instance不为null时,instance的引用对象可能还没有完成
    初始化(但是内存已经被分配出去了)

    instance = new Instance()可以分成以下三步:

    memory = allocate();// 1.分配对象内存空间
    instance(memory);// 2.初始化对象
    instance = memory;// 3.设置instance指向刚刚分配的内存地址,此时instance!=null;
    

    步骤2,3不存在数据依赖关系,所以可以进行重排优化
    因此,需要加volatile修饰

    private static volatile Test002 instance = null;
    
  • 相关阅读:
    5.小程序-生命周期函数
    4.小程序-路由跳转
    3.小程序-事件绑定
    2.小程序-数据绑定
    1.小程序index页静态搭建
    小程序简介
    单链表(Go)
    输入一个字符串,里面有26个英文字母和(半角逗号半角空格半角句号)按照()里的内容进行分割,遇到大写字母把其变成小写,遇到小写的将其变成大写然后输出字符串
    排序算法
    单例模式
  • 原文地址:https://www.cnblogs.com/zhangyuanbo/p/14188532.html
Copyright © 2011-2022 走看看