[Java] [Singleton] [DCL]

Posted ArancioneRagazza

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java] [Singleton] [DCL]相关的知识,希望对你有一定的参考价值。

Singleton

  • 只能有一个实例;必须自己创建自己的实例;必须给其他所有对象提供这一实例

实现方法

饿汉式singleton

  • 预先加载法
  • class Single {
      private Single() {
        System.out.println("ok");
      }
      
      private static Single instance = new Single();
      
      public static Single getInstance() {
        return instance;
      }
    }
  • 优点:
    • thread safe
    • 调用时速度快(在类加载时已经创建好一个static对象)
  • 缺点:
    • 资源利用率不高(可能系统不需要)
    • 在一些场景下无法使用。比如在single实例的创建依赖参数或配置文件时。

懒汉式singleton

  • 延迟加载法
  • public class LazySingleton {
      private static LazySingleton instance;
      private LazySingleton() {}
      
      public static LazySingleton getInstance() {
        if (instance == null) {
          instance = new LazySingleton();
        }
        return instance;
      }
    }
  • 适用于单线程环境,not trhead-safe,getInstance()方法可能返回两个不同实例。
  • 可以改成thread-safe版本,如下:
    public class LazySingleton {
        private static LazySingleton instance;    
        private LazySingleton() {}
        
        public static synchronized LazySingleton getInstance() {             
    		if (instance == null) {                       
    			instance = new LazySingleton();           
    		}
            return instance;                                      
        }
    }
  • 优点:不执行getInstance对不会被实例化
  • 缺点:第一次加载时反应不快。每次调用getInstance的同步开销大。(大量不必要的同步)

DCL singleton

  • Double Check Lock
  • 避免每次调用getInstance方法时都同步
  • public class LazySingleton {
      private static LazySingleton instance;
      private LazySingleton() {}
      
      public static LazySingleton getInstance() {
        if (instance == null) {
          synchronized(LazySingleton.class) {
            if (instance == null) {
              instance = new LazySingleton();
            }
          }
        }
        return instance;
      }
    }
  • 第一层判断,避免不必要的同步。第二层判断则是在线程安全的情况下创建实例。
  • 优点:资源利用率高,多线程下效率高。
  • 缺点:第一次加载时反应不快,由于java内存模型一些原因偶尔会失败,在高并发下有一定的缺陷。
  • 上述代码依然存在不安全性
    instance = new LazySingleton()这条语句实际上不是一个原子操作,它大概包括三件事:
    1. 给LazySingleton的实例分配内存;
    2. 初始化LazySingleton()的构造器;
    3. 将instance对象指向分配的内存空间(在这一步的时候instance变成非null)。

  但是由于Java编译器允许处理器乱序执行(指令重排序),上述2、3点的顺序是无法保证的。(意思是可能instance != null时有可能还未真正初始化构造器)。 
  解决方法是通过将instance定义为volatile的。(volatile有两个语义:1. 保证变量的可见性;2. 禁止对该变量的指令重排序)

  • 参考<<Java并发编程>> P286 ~ P287。在JMM后续版本(>= Java5.0)中,可以通过结合volatile的方式来启动DCL,并且该方式对性能的影响很小。然而,DCL的这种使用方式已经被广泛地抛弃了。
  • (因为volatile屏蔽指令重排序的语义在JDK1.5中才被完全修复,此前的JDK中即使将变量声明为volatile,也仍然不能完全避免重排序所导致的问题,这主要是因为volatile变量前后的代码仍然存在重排序问题。)

static内部类singleton

  • class Single {
      private Single() {}
      
      private static class InstanceHolder {
        private static final Single instance = new Single();
      }
      
      public static Single getInstance() {
        return InstanceHolder.instance();
      }
    }
  • 优点:线程安全,资源利用率高。
  • 缺点:第一次加载时反应不快。
  • 原理:类级内部类(static修饰的成员内部类)只有在第一次使用时才会被加载

Summary

  • 考虑到效率、安全性等问题,一般常用饿汉式singleton or static内部类singleton。其中后者是常用的singleton实现方法。

 




以上是关于[Java] [Singleton] [DCL]的主要内容,如果未能解决你的问题,请参考以下文章

懵了,Java枚举单例模式比DCL和静态单例要好???

Java枚举单例模式比DCL和静态单例要好?

单例模式——DCL失效问题

Java枚举单例模式比DCL和静态单例要好?———反编译分析单例枚举类

Java枚举单例模式比DCL和静态单例要好?———反编译分析单例枚举类

java--jmm知识