单例模式

简介: 所有单例模式都有一个共性,那就是这个类没有自己的状态。也就是说无论这个类有多少个实例,都是一样的;然后除此者外更重要的是,这个类如果有两个或两个以上的实例的话程序会产生错误。基于上述原因,非线程安全的实现方式,在此不再讨论。

所有单例模式都有一个共性,那就是这个类没有自己的状态。也就是说无论这个类有多少个实例,都是一样的;然后除此者外更重要的是,这个类如果有两个或两个以上的实例的话程序会产生错误。

基于上述原因,非线程安全的实现方式,在此不再讨论。下面讨论的都是线程安全的一些实现方式和存在的问题。

双重加锁模式

public class Singleton{
  private volatile static Singleton singleton;
  private Singleton(){

  }

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

双重加锁模式相对于普通的单例和加锁模式而言,从性能和线程安全上来说都有很大的提升和保障。然而双重加锁模式也存在一些隐蔽不易被发现的问题。首先我们要明白在JVM创建新的对象时,主要要经过三个步骤。

    • 分配内存
    • 初始化构造器
    • 将对象指向分配的内存地址

这样的顺序在双重加锁模式下是么有问题的,对象在初始化完成之后再把内存地址指向对象。但是现代的JVM会针对字节码进行调优,这样的话就有可能导致2和3的顺序是相反的,一旦出现这样的情况问题就来了。

所以更加理想的方案是利用静态内部类的方式来创建,因为静态属性由JVM确保第一次初始化时创建,因此也不用担心并发的问题出现。当初始化进行到一半的时候,别的线程是无法使用的,因为JVM会帮我们强行同步这个过程。另外由于静态变量只初始化一次,所以singleton仍然是单例的。

静态内部类的方式

public class Singleton{

  private Singleton(){}

  public static Singleton getInstance(){
    return InnerClassSingleton.singleton;
  }

  private class InnerClassSingleton{
    protected static Singleton singleton = new Singleton();
  }
}

然而,虽然静态内部类模式可以很好地避免并发创建出多个实例的问题,但这种方式仍然有其存在的隐患。

    • 一旦一个实例被持久化后重新生成的实例仍然有可能是不唯一的。
    • 由于java提供了反射机制,通过反射机制仍然有可能生成多个实例。

单例最优方案,枚举的方式

枚举实现单例的优势

  • 自由序列化;
  • 保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);
  • 线程安全;
public enum Singleton {
    INSTANCE;

    private Singleton(){}
}

参考文档:

相关文章
|
7月前
|
设计模式 安全 测试技术
【C++】—— 单例模式详解
【C++】—— 单例模式详解
|
2月前
|
C++
C++单例模式
C++中使用模板实现单例模式的方法,并通过一个具体的类A示例展示了如何创建和使用单例。
31 2
|
4月前
|
存储 设计模式 测试技术
单例模式
单例模式
|
设计模式 安全 编译器
2023-6-12-第三式单例模式
2023-6-12-第三式单例模式
73 0
找对象需要单例模式吗?
单例模式的类只提供私有的构造函数
|
SQL 安全 Java
五种单例模式介绍
五种单例模式介绍
92 0
|
安全 Java
单例模式很简单
《基础系列》
117 0
单例模式很简单
|
存储 安全 调度
单例模式的简单介绍
单例模式的简单介绍
|
安全 Java 容器
单例模式的应用(3)
单例模式的应用(3)
173 0
|
设计模式 开发框架 Java
单例模式7种实现
单例模式7种实现
154 0