zoukankan      html  css  js  c++  java
  • 单例模式中的多线程分析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;
        }
    }
  • 相关阅读:
    使用golang访问kubebernetes
    使用 Rancher 管理现有 Kubernetes 集群
    Running powershell scripts during nuget package installation and removal
    How to Create, Use, and Debug .NET application Crash Dumps in 2019
    寻找写代码感觉(一)之使用 Spring Boot 快速搭建项目
    Selenium+Java之解决org.openqa.selenium.InvalidArgumentException: invalid argument报错问题
    Selenium环境搭建
    关于Xpath定位方法知道这些基本够用
    Web自动化之浏览器启动
    【翻译】编写代码注释的最佳实践
  • 原文地址:https://www.cnblogs.com/fengjian/p/4345686.html
Copyright © 2011-2022 走看看