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

    1. 全局变量的缺点:

     

       必须在程序一开始就创建好对象,如果程序在这次的执行过程中又一直没用到它,就非常耗费资源。

     

    2. 经典的单例模式实现:

     

     

    Java代码
    1. public class Singleton {    
    2.       //用一个静态变量来记录Singleton类的唯一实例   
    3.       private static Singleton uniqueInstance;   
    4.     
    5.       private Singleton() {}   
    6.            
    7.       //注意这个方法也是静态的   
    8.       public static Singleton getInstance()
    9.  {    
    10.            if(uniqueInstance == null) {   
    11.              uniqueInstance = new Singleton();   
    12.            }   
    13.            return uniqueInstance;   
    14.       }   
    15. }  

     

     

        单例常被用来管理共享的资源,例如数据库连接、线程池、缓存、注册表。

        单例模式确保一个类只有一个实例,并提供一个全局访问点。

     

        这个模式的问题:在多线程时,并不能保证这个类只被实例化一次。

     

    3. 处理多线程:

     

        public class Singleton {

    Java代码
    1.     //用一个静态变量来记录Singleton类的唯一实例   
    2.     private static Singleton uniqueInstance;   
    3.     
    4.     private Singleton() {}   
    5.            
    6.     //注意这个方法也是静态的   
    7.     public static synchronized Singleton getInstance() {    
    8.         if(uniqueInstance == null) {   
    9.              uniqueInstance = new Singleton();   
    10.          }   
    11.          return uniqueInstance;   
    12.     }   
    13. }  

       通过增加synchronized关键字到getInstance()方法中,迫使每个线程在进入方法之前,要先等别的线程离开该方法。也就是说,不会有两个线程可以同时进入这个方法。

     

       这种方法存在的问题:只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种浪费。

     

    4.改善多线程

     

    4.1 如果getInstance()的性能对应用程序不是很关键,就不用优化了

     

    4.2 使用急切创建实例,而不用延迟实例化的做法

     

     

    Java代码
    1. public class Singleton {    
    2.   
    3.     private static Singleton uniqueInstance = new Singleton();  
    4.     
    5.     private Singleton() {}   
    6.            
    7.     public static Singleton getInstance() {    
    8.          return uniqueInstance;   
    9.     }   
    10. }  
     

       标红的语句在静态初始化器(static initializer)中创建单例,这保证了线程安全

       利用这个做法,JVM在加载这个类时马上创建此唯一的单件实例。JVM保证任何线程访问uniqueInstance静态变量之前,一定先创建些实例。

     

       4.3 用“双重检查加锁”,在getInstance()中减少使用同步

     

        首先检查实例是否已经创建,如果尚未创建,才进行同步。这样一来,只有第一次会同步,这正是我们想要的。

     

     

    Java代码
    1. public class Singleton {    
    2.   
    3.     private volatile static Singleton uniqueInstance;   
    4.     
    5.     private Singleton() {}   
    6.            
    7.     public static Singleton getInstance() 
    8. {    
    9.     if(uniqueInstance == null) { //(1)   
    10.         //只有第一次才彻底执行这里的代码   
    11.        synchronized() {   
    12.           //再检查一次   
    13.           if(uniqueInstance == null)   
    14.         uniqueInstance = new Singleton();   
    15.        }   
    16.     }   
    17.          return uniqueInstance;   
    18.     }   
    19. }  

       在最开始如果有1、2、3个线程走到了(1)处,假设1进入了同步块,2、3等待。1实例化后,2进入同步块,发现uniqueInstance已经不为空,跳出同步块。接着3进入,又跳出同步块。

     

        volatile关键字确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地uniqueInstance变量。如果性能是你关心的重点,那么这个做法可以帮你大大地减少getInstance()的时间耗费。

    new对象只有第一次才会new,new的过程要同步,确保只new出来一个


    第一个if(uniqueInstance == null), 确保只在第一次才会同步new出来对象


    第二个if(uniqueInstance == null),确保如果多个线程已经进入第一个if(uniqueInstance == null),同时等待synchronized的执行,但是第一个进入synchronized的已经new出来了,此刻不需要再new,故进入synchronized之后,再检查一遍if。涉及线程切换的问题。




  • 相关阅读:
    怎样提高开发效率
    ASP.NET/C#获取文章中图片的地址
    listBox的用法
    ASP.NET中的asp:label和asp:literal
    ID,ClientID,UniqueID的区别
    asp.net中的属性
    数据库中的值为空的判断 ,并赋初值
    属性器,转换从数据库中读取的状态
    Collections.emptyList() and Collections.EMPTY_LIST
    InnoDB
  • 原文地址:https://www.cnblogs.com/baoendemao/p/3804726.html
Copyright © 2011-2022 走看看