• 单例模式中的多线程分析synchronized


    谈到单例模式,我们立马会想到饿汉式和懒汉式加载,所谓饿汉式就是在创建类时就创建好了实例,懒汉式在获取实例时才去创建实例,即延迟加载。

    饿汉式:

    Java代码  
     1 package com.bijian.study;
     2 
     3 public class Singleton {
     4 
     5     private static Singleton instance = new Singleton();
     6 
     7     public static synchronized Singleton getInstance() {
    
    13         return instance;
    14     }
    15 }

    懒汉式:

    Java代码  
     1 package com.bijian.study;
     2 
     3 public class Singleton {
     4 
     5     private static Singleton instance = null;
     6 
     7     public static synchronized Singleton getInstance() {
     8 
     9         // 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
    10         // 使用时生成实例,提高了效率!
    11         if (instance == null)
    12             instance = new Singleton();
    13         return instance;
    14     }
    15 }

         上面第二中形式的lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。

    注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。一般认为第一种形式要更加安全些。

           对于上面的懒汉式方式,从多线程角度来看,Synchronized放到方法上会影响性能。于是我们不难想到将其放到方法里。

    Java代码  
    public class Singleton {
        private static Singleton instance = null;
    
        private static String lock = "";
    
        public static Singleton getInstance() {
            if (instance == null)
                synchronized (lock) {
                    instance = new Singleton();
                }
            return instance;
        }
    }

    我们稍加分析,不难发现,这样会存在线程安全问题,假如线程一刚好执行完if (instance == null)的判断语句(还未加锁),调度至线程二也执行这条判断语句,也是true,也进入了if的语句块中,这样就会产生两个实例,而非单实例了。

    此时,我们稍加分析,那还不容易,在锁里面再加一个是否为空的判断,即所谓的double-checked locking (DCL),如下所示:

    Java代码  
    package com.bijian.study;
    
    public class Singleton {
    
        private static Singleton instance = null;
        private static String lock = new String();
    
        public static Singleton getInstance() {
    
            // 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
            // 使用时生成实例,提高了效率!
            if (instance == null)
                synchronized(lock) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            return instance;
        }
    }

         此时,很多人都会觉得无懈可击了。从多线程角度来看,这样写的单例确实没有问题了,但从Java的类创建原理来看,可能还有问题。从浅显简单的理解来看,就是对象还未完全创建出来,但instance变量已被赋值,此时另一个线程获取实例时,会得到instance,但它的堆空间及相关的方法还未完成时,调用实例方法就会出错。

           啊?还存在这样的问题呀?这可以虚拟机的实现问题,难道还要我考虑?是的,其实稍加改动,就可以避免这样的问题。

           解决办法,加一个局部变量,如下所示:

    Java代码  
    package com.bijian.study;
    
    public class Singleton {
    
        private static Singleton instance = null;
        private static String lock = new String();
    
        public static Singleton getInstance() {
    
            // 这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
            // 使用时生成实例,提高了效率!
            if (instance == null)
                synchronized(lock) {
                    if (instance == null) {
                        Singleton temp = new Singleton();
                        instance = temp;
                    }
                }
            return instance;
        }
    }
  • 相关阅读:
    随笔:金融的“游戏”规则——游戏世界的区块链喵与现实世界的金融科技
    js实现链表
    事件
    JQ操作DOM
    JQuery选择器
    AJAX
    file
    表单
    DOM
    window&navigator&screen&location
  • 原文地址:https://www.cnblogs.com/fengjian/p/4345686.html
Copyright © 2020-2023  润新知