# 单例模式

## Java实现

### 饿汉式、线程安全、最简单的方式

```java
/**
 * 静态变量实现方式、线程安全、饿汉式； 是最常用的一种方式
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S1_HungerImplement {

    private static final S1_HungerImplement INSTANCE = new S1_HungerImplement();

/*    static {
        INSTANCE = new S1_StaticFieldImplements();
    }*/

    private S1_HungerImplement() {
    }

    public static S1_HungerImplement getInstance() {
        return INSTANCE;
    }
}
```

### 懒汉式、非线程安全、最简单的方式

```java
/**
 * 懒汉式，线程不安全
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S2_LazyLoadingImplement {

    private static S2_LazyLoadingImplement INSTANCE;

    private S2_LazyLoadingImplement() {
    }

    public static S2_LazyLoadingImplement getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new S2_LazyLoadingImplement();
        }
        return INSTANCE;
    }

}
```

### 懒汉式、线程安全、同步方法

```java
/**
 * 懒汉式、线程安全
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S3_LazyThreadSafeImplement {

    private static S3_LazyThreadSafeImplement INSTANCE;

    private S3_LazyThreadSafeImplement() {
    }

    public static synchronized S3_LazyThreadSafeImplement getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new S3_LazyThreadSafeImplement();
        }
        return INSTANCE;
    }

}
```

### 懒汉式、线程安全、双重检查锁

同步方法的线程安全模式，缺点很明显，因为对象的创建之前，可能会做很多耗时操作，所以优化如下：

```java
/**
 * 懒汉式、线程安全, 减少了同步代码快的区域
 * 此模式有问题，当多线程并发访问有问题，因为判断与初始化不是一个原子操作，有可能初始化多次
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S4_LazyThreadSafeImplement2 {

    private static S4_LazyThreadSafeImplement2 INSTANCE;

    private S4_LazyThreadSafeImplement2() {
    }

    public static S4_LazyThreadSafeImplement2 getInstance() {
        if (INSTANCE == null) {
            synchronized (S4_LazyThreadSafeImplement2.class) {
                INSTANCE = new S4_LazyThreadSafeImplement2();
            }
        }
        return INSTANCE;
    }

}
```

但是这种方式，是一种错误的实现方式，因为判断与初始化不是一个原子操作，有可能初始化多次。所以，使用双重检查锁替换该方式：

```java
/**
 * 懒汉式、线程安全, 双重检查锁方式
 * 为了解决S4的问题，我们可以在synchronized内部再判断一次, 既缩小了同步区域，又保证了不会被初始化多次
 * volatile用于禁止指令重排序
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S5_DoubleCheckLockImplement {

    private static volatile S5_DoubleCheckLockImplement INSTANCE;

    private S5_DoubleCheckLockImplement() {
    }

    public static S5_DoubleCheckLockImplement getInstance() {
        if (INSTANCE == null) {
            synchronized(S5_DoubleCheckLockImplement.class) {
                if (INSTANCE == null) {
                    INSTANCE = new S5_DoubleCheckLockImplement();
                }
            }
        }
        return INSTANCE;
    }

}
```

### 懒汉式、静态内部类

```java
/**
 * 静态内部类方式，懒汉式，线程安全
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S6_StaticInnerClassImplement {

    private S6_StaticInnerClassImplement() {
    }

    private interface InstanceHolder {
        S6_StaticInnerClassImplement INSTANCE = new S6_StaticInnerClassImplement();
    }

    // 当调用了getInstance方法时，类加载器才会去加载InstanceHolder类，从而才会初始化成员 INSTANCE
    public static S6_StaticInnerClassImplement getInstance() {
        return InstanceHolder.INSTANCE;
    }

}
```

### 懒汉式、Enum实现、比较完美

```java
/**
 * 最完美的方式
 * 枚举实现，饿汉式，线程安全. 可防止反序列化(因为枚举类没有构造方法)
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public enum S7_EnumImplement {

    INSTANCE;

    @Override
    public String toString() {
        return getDeclaringClass().getCanonicalName() + "@" + hashCode();
    }
}
```

### 饿汉式、容器单例

```java
/**
 * 容器单例， 比如spring容器
 *
 * @author yangsx
 * @version 1.0
 * @date 2019/9/11
 */
public class S8_ContainerImplement {

    private static List<S8_ContainerImplement> set = new ArrayList<>();

    static {
        set.add(new S8_ContainerImplement());
    }

    public static S8_ContainerImplement getInstance() {
        return set.get(0);
    }
}
```

## Golang实现

### 饿汉式、并发安全

```go
type singleton struct{}
var ins *singleton = &singleton{}
func GetIns() *singleton{
    return ins
}
```

### 懒汉式、并发不安全

```go
type singleton struct{}
var ins *singleton
func GetIns() *singleton{
  if ins == nil {
        ins = &singleton{}
    }
    return ins
}
```

### 懒汉式、并发安全、加锁

```go
type singleton struct{}
var ins *singleton
var mu sync.Mutex
func GetIns() *singleton{
    mu.Lock()
    defer mu.Unlock()

    if ins == nil {
    　　ins = &singleton{}
    }
    return ins
}
```

### 懒汉式、并发安全、双重检查锁

```go
type singleton struct{}
var ins *singleton
var mu sync.Mutex
func GetIns() *singleton{  
　　if ins == nil {
   　　mu.Lock()
      defer mu.Unlock()
      if ins == nil {
         ins = &singleton{}
      }
   }
   return ins
}
```

### 懒汉式、并发安全、sync.Once

```go
type singleton struct{}
var ins *singleton
var once sync.Once
func GetIns() *singleton {
    once.Do(func(){
        ins = &singleton{}
    })
    return ins
}
```

## JDK中的单例模式

* `java.lang.Runtime#getRuntime()`，饿汉式、线程安全、最简单的方式
* `java.awt.Desktop#getDesktop()`，懒汉式、线程安全、同步方法
* `java.lang.System#getSecurityManager()`

## MyBatis ErrorContext

ErrorContext类用于记录本次执行过程中相关上下文信息，待发生Error时候其他组件就可以从本类实例中获取到相关的上下文信息。因为使用了`ThreadLocal<T>`, 我们就能直接取到之前执行本SQL的线程上的信息, 也就很方便的构建出异常发生时的上下文，快速排错

```java
public class ErrorContext {

  private static final String LINE_SEPARATOR = System.lineSeparator();
  // 将实例放入到线程本地变量中，饿汉
  private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);

  private ErrorContext stored;
  private String resource;
  private String activity;
  private String object;
  private String message;
  private String sql;
  private Throwable cause;

  // 构造函数私有化
  private ErrorContext() {
  }
  
  // 获取实例，也就是错误信息上下文
  public static ErrorContext instance() {
    return LOCAL.get();
  }

  public ErrorContext store() {
    ErrorContext newContext = new ErrorContext();
    newContext.stored = this;
    LOCAL.set(newContext);
    return LOCAL.get();
  }

  public ErrorContext recall() {
    if (stored != null) {
      LOCAL.set(stored);
      stored = null;
    }
    return LOCAL.get();
  }

  public ErrorContext resource(String resource) {
    this.resource = resource;
    return this;
  }

  public ErrorContext activity(String activity) {
    this.activity = activity;
    return this;
  }

  public ErrorContext object(String object) {
    this.object = object;
    return this;
  }

  public ErrorContext message(String message) {
    this.message = message;
    return this;
  }

  public ErrorContext sql(String sql) {
    this.sql = sql;
    return this;
  }

  public ErrorContext cause(Throwable cause) {
    this.cause = cause;
    return this;
  }

  // thread-local在使用后，需要清空，mybatis通过try-catch-finally的机制(针对sql语句)，在finally块中调用此方法清空thread-local
  public ErrorContext reset() {
    resource = null;
    activity = null;
    object = null;
    message = null;
    sql = null;
    cause = null;
    LOCAL.remove();
    return this;
  }

  @Override
  public String toString() {
    StringBuilder description = new StringBuilder();

    // message
    if (this.message != null) {
      description.append(LINE_SEPARATOR);
      description.append("### ");
      description.append(this.message);
    }

    // resource
    if (resource != null) {
      description.append(LINE_SEPARATOR);
      description.append("### The error may exist in ");
      description.append(resource);
    }

    // object
    if (object != null) {
      description.append(LINE_SEPARATOR);
      description.append("### The error may involve ");
      description.append(object);
    }

    // activity
    if (activity != null) {
      description.append(LINE_SEPARATOR);
      description.append("### The error occurred while ");
      description.append(activity);
    }

    // sql
    if (sql != null) {
      description.append(LINE_SEPARATOR);
      description.append("### SQL: ");
      description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
    }

    // cause
    if (cause != null) {
      description.append(LINE_SEPARATOR);
      description.append("### Cause: ");
      description.append(cause.toString());
    }

    return description.toString();
  }

}
```


---

# 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://yangsx95.gitbook.io/notes/computer-science/she-ji-mo-shi/dan-li-mo-shi.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.
