Singleton Pattern — 单例模式

  单例模式是用来创建一个只能又一个实例的对象。

  单例模式类图如下。

 

 

 

 

  单例模式通用代码(饿汉模式):

public class Singleton {
        
    private static Singleton singleton = new Singleton();
      
    // 限制产生多个对象
    private Singleton() {}

    //通过该方法获取实例对象
    public static Singleton getInstance() {
        return singleton;
    }

    //类中其他方法,尽量是static
    public static void doSomething() {}

}

  延时单例模式(懒汉模式)

public class Singleton {
        
    private static Singleton singleton = null;
      
    // 限制产生多个对象
    private Singleton() {}

    //通过该方法获取实例对象
    public static Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
         }
        return singleton;
    }

    //类中其他方法,尽量是static
    public static void doSomething() {}

} 

  

  单例模式和线程

    以上两种模式都属于线程不安全,在不是高并发的条件下,基本不会出现问题,但是当并发量增加时可能回出现多个实例,破坏最处当预期。

    为什么会出现这种情况呢?

      如果一个A执行到singleton = new Singleton(),但还没有获得对象(对象初始化时需要时间的),在此同时第二个线程B也在执行,执行到

     (singleton == null)判断,那么线程B判断的结果也为真。于是继续运行下去,线程A获得了一个对象,线程B也获得了一个对象,

      在内存中就出现了两个对象!

   

    1、只要在getInstance() 方法前加 synchronized 关键字就能解决该问题。

public class Singleton {
        
    private static Singleton singleton = null;
      
    // 限制产生多个对象
    private Singleton() {}

    //通过该方法获取实例对象
    //通过添加synchronized关键字到getInstance()方法中,迫使每个线程    
    //访问该方法之前,要等其他线程从该方法中离开。
    public static synchronized Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
         }
        return singleton;
    }

    //类中其他方法,尽量是static
    public static void doSomething() {}

} 

  解决了线程安全问题,但是使用同步会降低性能,(如果你的系统能接受getInstance()方法带来的额外性能负担,这样的做法简单有效。)

  同步可能造成执行效率下降100倍。因此,如果getInstance()的程序使用非常频繁就得重新考虑了。

  2、用“双重检查加锁”,在getInstance中减少使用同步。

    利用双重检查加锁(double-checkd locking),首先检查是否有实例已创建,如果尚未创建,  

    “才”进行同步,这样一来只有第一次会进行同步,这正是我们需要的。

public class Singleton {
     //volatile关键字确保:当singleton变量被初始化成
     //Singleton实例时,多个线程正确当处理singleton变量。 
    private volatile static Singleton singleton;
          
    private Singleton() {}

    public static Singleton getInstance() {
        
        if(singleton == null) {//检查实例,如果不存在就进入同步区块。
            //只有第一次才会彻底执行此处代码。
            synchronized (Singleton.class) {
                  //进入区块再做一次判读,如果位null才会创建。  
                 if(singleton == null) {
                    singleton = new Singleton();
                }    
            }
         }
        return singleton;
    }

    //类中其他方法,尽量是static
    public static void doSomething() {}

}     

  如果你关心性能,这种做法可以大大减少getInstance()的耗时。

 

 

注意:双重检查加锁不实用1.4以前的Java。

  Java1.2前会造成,单利在没有全局的引用时被当作垃圾回收掉。Java1.2以后这个问题已被修复了。

 

 

 

    

版权声明:本文为klyjb原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/klyjb/p/11493314.html