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;
    
  • 相关阅读:
    第一个EJB示例
    Visual Studio/Eclipse调用 JBoss5中的WebService
    TomEE
    eclipse 启动时使用指定的jdk
    Haskell示例
    安装VS2010 SP1后,再安装mvc3
    Mysql报错为1366的错误的时候
    Java8-如何将List转变为逗号分隔的字符串
    IDEA连接mysql又报错!Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezone' prope
    he last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
  • 原文地址:https://www.cnblogs.com/zhangyuanbo/p/14188532.html
Copyright © 2011-2022 走看看