Singleton单例模式

Singleton(单例模式):

单例模式用来保证整个程序中某个实例有且只有一个。

单例模式有两种构建方式:

  • 饿汉方式:用static修饰,在类装载时构建。不适用于初始化时构造器非常耗时的类。
  • 懒汉方式:指全局的单例实例只有在第一次被使用时才构建,延迟初始化。

1.饿汉式单例类

public class TestSingleton
 
    private static TestSingleton instance = new TestSingleton();
 
    //把默认的构造方法设置为私有的,这样外界就无法通过构造方法来创建实例
    private TestSingleton() {}
 
    //静态方法,用来返回类的唯一实例
public static TestSingleton getInstance(){
return instance;
}
}
  • 饿汉模式因为是用static修饰,在类装载时构建,所以会导致加载类时比较慢,如果构造器内的方法比较耗时,则加载过程会比较长。
  • 但饿汉模式是线程安全的,运行时获取对象速度比较快。

2.懒汉式单例类

public class TestSingleton {
    //把默认的构造方法设置为私有的,这样外界就无法通过构造方法来创建实例
    private TestSingleton() {
    }

    private static TestSingleton instance = null;

    //静态方法,用来返回类的唯一实例,因为用synchronized加锁力度太大,所以使用double check的方式。
    public static TestSingleton getInstance() {
        if (instance == null) {
            synchronized (TestSingleton.class) {
                if (instance == null) {

                    //instance在初始化过程中,可能已经不为null,所以加一临时变量t,确保初始化完成后再赋值给instance。
                     TestSingleton t = new TestSingleton();
                     instance = t;
                 }
            }
        }
        return instance;
        }
}

其中instance = new TestSingleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

  1. 给 instance 分配内存
  2. 调用TestSingleton 的构造函数来初始化成员变量,形成实例
  3. 将instance对象指向分配的内存空间(执行完这步 instance才是非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,就会报错,所以我们使用一个临时变量,确保初始化完成后再赋值给instance。

  • 懒汉模式的特点是加载类时比较快,但是在运行时获取对象的速度比较慢,线程不安全, 懒汉式如果在创建实例对象时不加上synchronized则会导致对象的访问不是线程安全的。

发表评论

邮箱地址不会被公开。 必填项已用*标注