单例模式比较好理解,就是保证只有一个对象被创建
如果是单线程下代码还比较好写一些:
/**
*饿汉模式
* 单例实例在类装载时进行创建
* 线程安全
*/
public class SingletonExample2 {
// 私有构造函数
private SingletonExample2(){
}
// 单例对象
private static SingletonExample2 instance = new SingletonExample2();
// 静态的工厂方法
public static SingletonExample2 getInstance(){
return instance;
}
}
上面的是饿汉模式,在类加载时就开始创建
懒汉模式就比较懒,在使用的时候才会创建
/**
*懒汉模式
* 单例实例在第一次使用时进行创建
* 线程安全--并不推荐,因为带来了性能上的开销
*/
public class SingletonExample3 {
// 私有构造函数
private SingletonExample3(){
}
// 单例对象
private static SingletonExample3 instance = null;
// 静态的工厂方法
public static synchronized SingletonExample3 getInstance(){
if (instance == null){
instance = new SingletonExample3();
}
return instance;
}
}
如果是在单线程环境下,上面的代码是没有任何问题的
但是如果是在多线程环境下呢?
现在同时有线程 A 和 B 要创建线程,此时线程 A 拿到了 instance 的值为 null ,然后 CPU 停止了当前线程 A ,线程 B 开始运行,也拿到了 instance 的值为 null ,接下来线程 A 和 B 都会去创建
怎么办呢?加个双重检测机制,再加个锁
/**
*懒汉模式-->双重同步锁模式
* 单例实例在第一次使用时进行创建
* 线程不安全
*/
public class SingletonExample4 {
// 私有构造函数
private SingletonExample4(){
}
// 1, memory = allocate() 分配对象的内存空间
// 2, ctorInstance() 初始化对象
// 3, instance = memory 设置 instance 指向刚分配的内存
// JVM 和 cpu 优化时,会发生指令重排
// 有可能发生 1,3,2
//此时会导致线程不安全
// 单例对象
private static SingletonExample4 instance = null;
// 静态的工厂方法
public static SingletonExample4 getInstance(){
if (instance == null){
// 使用了双重检测机制
synchronized (SingletonExample4.class) {
// 同步锁
if(instance == null) {
instance = new SingletonExample4();
}
}
}
return instance;
}
}
这样双重检测再加锁看似是没有问题了
但是还记得吗? JVM 是会指令重排的,本来 1,2,3 运行下来没问题,结果呢,指令重排之后就变成了 1,3,2 ,还是两个线程,线程 A 拿到第一个的 instance 为 null ,线程 B 拿到的是指令重排之后的第二个 instance 为 null ,此时仍然有线程不安全的问题
那我不让它指令重排不就好了? volatile 就要大显身手了
/**
*懒汉模式-->双重同步锁模式
* 单例实例在第一次使用时进行创建
*/
public class SingletonExample5 {
// 私有构造函数
private SingletonExample5(){
}
// 1, memory = allocate() 分配对象的内存空间
// 2, ctorInstance() 初始化对象
// 3, instance = memory 设置 instance 指向刚分配的内存
// JVM 和 cpu 优化时,会发生指令重排
// 有可能发生 1,3,2
//此时会导致线程不安全
// 单例对象--- volatile + 双重检测机制-->禁止指令重排
private volatile static SingletonExample5 instance = null;
// 静态的工厂方法
public static SingletonExample5 getInstance(){
if (instance == null){
// 使用了双重检测机制
synchronized (SingletonExample5.class) {
// 同步锁
if(instance == null) {
instance = new SingletonExample5();
}
}
}
return instance;
}
}
上面都是对懒汉模式的优化,饿汉模式也想线程安全,该咋办嘞
/**
*饿汉模式
* 单例实例在类装载时进行创建
* 线程安全--->要注意 private static 和 static 的先后执行顺序
*/
public class SingletonExample6 {
// 私有构造函数
private SingletonExample6(){
}
// 单例对象
private static SingletonExample6 instance = null;
static{
instance = new SingletonExample6();
}
// 静态的工厂方法
public static SingletonExample6 getInstance(){
return instance;
}
public static void main(String[] args) {
System.out.println(getInstance().hashCode());
System.out.println(getInstance().hashCode());
}
}
还有一种最安全的方法:
/**
* 线程安全---最安全
* 推荐使用
*/
public class SingletonExample7 {
// 私有构造函数
private SingletonExample7(){
}
public static SingletonExample7 getInstance(){
return Singleton.INSTANCE.getInstance();
}
private enum Singleton{
INSTANCE;
private SingletonExample7 singleton;
// JVM 保证这个方法绝对只调用一次
Singleton(){
singleton = new SingletonExample7();
}
public SingletonExample7 getInstance(){
return singleton;
}
}
}
以上,感谢您的阅读哇~