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 件事情:
- 给 instance 分配内存
- 调用TestSingleton 的构造函数来初始化成员变量,形成实例
- 将instance对象指向分配的内存空间(执行完这步 instance才是非 null 了)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,就会报错,所以我们使用一个临时变量,确保初始化完成后再赋值给instance。
- 懒汉模式的特点是加载类时比较快,但是在运行时获取对象的速度比较慢,线程不安全, 懒汉式如果在创建实例对象时不加上synchronized则会导致对象的访问不是线程安全的。