单例模式

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;
    }
}

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

/**
 * 懒汉式,线程不安全
 *
 * @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;
    }

}

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

/**
 * 懒汉式、线程安全
 *
 * @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;
    }

}

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

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

/**
 * 懒汉式、线程安全, 减少了同步代码快的区域
 * 此模式有问题,当多线程并发访问有问题,因为判断与初始化不是一个原子操作,有可能初始化多次
 *
 * @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;
    }

}

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

/**
 * 懒汉式、线程安全, 双重检查锁方式
 * 为了解决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;
    }

}

懒汉式、静态内部类

/**
 * 静态内部类方式,懒汉式,线程安全
 *
 * @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实现、比较完美

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

    INSTANCE;

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

饿汉式、容器单例

/**
 * 容器单例, 比如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实现

饿汉式、并发安全

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

懒汉式、并发不安全

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

懒汉式、并发安全、加锁

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
}

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

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

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的线程上的信息, 也就很方便的构建出异常发生时的上下文,快速排错

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();
  }

}

最后更新于