单例模式


1、懒汉式

①.懒汉式-线程不安全

  • 线程不安全,Lazy加载
  • 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程
  • 因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
    ` java
    public class SlackerThreadNoSafe {
    private static SlackerThreadNoSafe slackerThreadNoSafe;

    private SlackerThreadNoSafe() {
    }

    public static SlackerThreadNoSafe getInstance() {

      if (slackerThreadNoSafe == null) {
          slackerThreadNoSafe = new SlackerThreadNoSafe();
      }
      return slackerThreadNoSafe;
    

    }

}

#### ②.懒汉式-线程安全
* 线程安全,Lazy加载
* 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是效率很低.  
* 优点:第一次调用才初始化,避免内存浪费。  
* 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。  
```  java
public class SlackerThreadSafe {
    private static SlackerThreadSafe slackerThreadSafe;

    private SlackerThreadSafe() {
    }

    public static synchronized SlackerThreadSafe getInstance() {
        if (slackerThreadSafe == null) {
            slackerThreadSafe = new SlackerThreadSafe();
        }
        return slackerThreadSafe;
    }
}  

2、饿汉式

  • 线程安全,非Lazy加载
  • 优点:没有加锁,执行效率会提高。
  • 缺点:类加载时就初始化,浪费内存。

    public class StarvingModel {
      private static StarvingModel starvingModel = new StarvingModel();
    
      private StarvingModel() {
      }
    
      public static StarvingModel getInstance() {
          return starvingModel;
      }
    }  
    

3、双检锁/双重校验锁

  • 线程安全, Lazy加载
  • 这种方式采用双锁机制,安全且在多线程情况下能保持高性能

    public class DoubleCheckedLocking {
      private static DoubleCheckedLocking doubleCheckedLocking;
      private DoubleCheckedLocking(){}
    
      public static DoubleCheckedLocking getInstance(){
          if (doubleCheckedLocking == null){
              synchronized (DoubleCheckedLocking.class){
                  if (doubleCheckedLocking == null){
                      doubleCheckedLocking = new DoubleCheckedLocking();
                  }
              }
          }
          return doubleCheckedLocking;
      }
    }
    

4、登记式/静态内部类

  • 线程安全 Lazy加载
  • 因为类在初始化的时候会获得一个锁,而读取静态变量是触发类初始化的条件
    `java
    public class InnerClassModel {
    private static class Single{
      private static final InnerClassModel INNER_CLASS_MODEL = new InnerClassModel();
    
    }
    private InnerClassModel(){}
    public static final InnerClassModel getInstance(){
      return Single.INNER_CLASS_MODEL;
    
    }
    }


## 5、枚举
* 线程安全.非Lazy加载
* 自动支持序列化机制,防止反序列化重新创建新的对象
* 绝对防止多次实例化
```java
public enum EnumModel {
    INSTANCE;

    public void doSomeThing() {

    }
    // you can use EnumModel.INSTANCE.doSomeThing();
    // you can only be a single case.
}

6、总结

  • 菜鸟说: 一般情况下,不建议使用懒汉式,建议使用饿汉式。只有在要明确实现 lazy loading 效果时,才会使用登记式。如果涉及到反序列化创建对象时,可以尝试使用枚举式。如果有其他特殊的需求,可以考虑使用双检锁式。

文章作者: kangshifu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kangshifu !
 上一篇
JVM内存区域 JVM内存区域
1. 线程独占区1.1 程序计数器(Program Counter Register) 程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行
2018-05-23
下一篇 
葬花吟 葬花吟
花谢花飞花满天,红消香断有谁怜?游丝软系飘春榭,落絮轻沾扑绣帘。闺中女儿惜春暮,愁绪满怀无释处。手把花锄出绣帘,忍踏落花来复去。柳丝榆荚自芳菲,不管桃飘与李飞;桃李明年能再发,明年闺中知有谁?三月香巢已垒成,梁间燕子太无情!明年花发虽可
2018-05-16
  目录