# Singleton Pattern

\[TOC]

## 单例模式

### 介绍

> 引用百度百科：
>
> 单例模式，属于创建类型的一种常用的[软件设计模式](https://baike.baidu.com/item/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/2117635)。通过单例模式的方法创建的类在当前进程中只有一个[实例](https://baike.baidu.com/item/%E5%AE%9E%E4%BE%8B/3794138)（根据需要，也有可能一个线程中属于单例，如：仅线程上下文内使用同一个实例）

### 追求的目标

* 线程安全
* 懒加载
* 调用效率高

### 方式

#### 饿汉

```java
public class Singleton {

    private static Singleton instance = new Singleton();
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return instance;
    }
}
```

#### 懒汉

```java
public class Singleton {
 
    private static Singleton instance;
 
    private Singleton() {}
 
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
```

#### 双重检测

```java
public class Singleton {
 
    private volatile static Singleton singleton;
 
    private Singleton() {}
 
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
```

**为什么要加volatile？**

因为`singleton = new Singleton();`无法保证原子性，为什么？通常Java文件将使用`javac`命令编译，得到Class，我们在使用`javap -c Singleton.class`，将获得Jvm指令，如下：

```java
Compiled from "Singleton.java"
public class Singleton {
  public static Singleton getSingleton();
    Code:
       0: getstatic     #2                  // Field singleton:LSingleton;
       3: ifnonnull     37
       6: ldc           #3                  // class Singleton
       8: dup
       9: astore_0
      10: monitorenter
      11: getstatic     #2                  // Field singleton:LSingleton;
      14: ifnonnull     27
      17: new           #3                  // class Singleton
      20: dup
      21: invokespecial #4                  // Method "<init>":()V
      24: putstatic     #2                  // Field singleton:LSingleton;
      27: aload_0
      28: monitorexit
      29: goto          37
      32: astore_1
      33: aload_0
      34: monitorexit
      35: aload_1
      36: athrow
      37: getstatic     #2                  // Field singleton:LSingleton;
      40: areturn
    Exception table:
       from    to  target type
          11    29    32   any
          32    35    32   any
}
```

熟知Synchronized原理的，很快便知道以下指令和代码是对应的：

> 不熟悉的也没事，我教你呀，飞机票：[Synchronized](https://atsjp.gitbook.io/note/java/sourceanalysis/jdk/concurrentprogramming/synchronized)

```java
      synchronized (Singleton.class) {
        if (singleton == null) {
        	singleton = new Singleton();
        }
      }

  		10: monitorenter
      11: getstatic     #2                  // Field singleton:LSingleton;
      14: ifnonnull     27
      17: new           #3                  // class Singleton             创建一个对象，分配内存
      20: dup																//                             复制栈顶数值并将复制值压入栈顶
      21: invokespecial #4                  // Method "<init>":()V         调用超类构造方法，实例初始化方法，私有方法
      24: putstatic     #2                  // Field singleton:LSingleton; 为指定的类的静态域赋值
      27: aload_0 												  // 														 将第一个引用类型本地变量推送至栈顶
      28: monitorexit

```

由此可见，`singleton = new Singleton();`可以为分为三步：1、创建对象，分配内存；2、实例初始化；3、变量引用实例。

同时，Jvm存在指令重排序情况，那么编译器可能做以下重排序：1、创建对象，分配内存；3、变量引用实例；2、实例初始化；那么在一个实例还没初始化结束的时候，此时已经被变量引用了，那么变量访问实例将会出问题。所以此处必须加上Volatile，因为Volatile的一个重要功能，就是告诉编译器禁止指令重排序。

> Volatile原理不明白？没关系，飞机票：[Volatile](https://atsjp.gitbook.io/note/java/sourceanalysis/jdk/concurrentprogramming/volatile)

#### 静态内部类

```java
public class Singleton {
 
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}	
```

#### 枚举

```java
public enum Singleton {
 
    INSTANCE;
 
}	
```

### 对比

|   方式  |      优点      |  缺点  |
| :---: | :----------: | :--: |
|   饿汉  |   线程安全、效率高   | 非懒加载 |
|   懒汉  |   线程安全、懒加载   |  效率低 |
|  双重检测 | 线程安全、懒加载、效率高 |   无  |
| 静态内部类 | 线程安全、懒加载、效率高 |   无  |
|   枚举  |   线程安全、效率高   | 非懒加载 |

所以推荐双重检测、静态内部类的写法，实际生产中，更推荐静态内部类的写法，因为便于书写逻辑不易犯错。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://atsjp.gitbook.io/note/java/designmodes/singletonpattern.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
