单例模式经常会听到的常用创建是饿汉模式和懒汉模式,今天在学习单例模式时借鉴了一些大佬的文章,记录下
1.饿汉模式
package com.bigbear.chapter012_singleton;
/**
* @author: yxb
* @date: 2022/10/18 21:59
* @version: v1.1
* @description: 单例-饿汉模式
* 优点:对象提前创建好了,使用的时候无需等待,效率高
* 缺点:对象提前创建,所以会占据一定的内存,内存占用大,以空间换时间
*/
public class Singleton_1 {
private final static Singleton_1 instance = new Singleton_1();
public static Singleton_1 getInstance() {
return instance;
}
}
2.懒汉模式
package com.bigbear.chapter012_singleton;
/**
* @author: yxb
* @date: 2022/10/18 22:08
* @version: v1.1
* @description: 懒汉模式
*/
public class Singleton_2 {
private static Singleton_2 instance;
private Singleton_2() {
}
public static synchronized Singleton_2 getInstance() {
if (null == instance) {
instance = new Singleton_2();
}
return instance;
}
}
3.double-check
package com.bigbear.chapter012_singleton;
/**
* @author: yxb
* @date: 2022/10/18 22:15
* @version: v1.1
* @description: double-check
* 双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行。
*
* 双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是这些习语失败的一个主要原因。
*
*
* singleton = new Singleton();
* 该语句非原子操作,实际是三个步骤。
*
* 1.给 Singleton 分配内存;
* 2.调用 Singleton 的构造函数来初始化成员变量;
* 3.将给 singleton 对象指向分配的内存空间(此时 singleton 才不为 null );
* 虚拟机的指令重排序–>
*
* 执行命令时虚拟机可能会对以上3个步骤交换位置 最后可能是132这种 分配内存并修改指针后未初始化 多线程获取时可能会出现问题。
*
* 当线程A进入同步方法执行singleton = new Singleton();代码时,恰好这三个步骤重排序后为1 3 2,
*
* 那么步骤3执行后 singleton 已经不为 null ,但是未执行步骤2,singleton对象初始化不完全,此时线程B执行 getInstance() 方法,
* 第一步判断时 singleton 不为null,则直接将未完全初始化的singleton对象返回了。
*
* 解决
* 如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的,同时还会禁止指令重排序
*
* 所以使用volatile关键字会禁止指令重排序,可以避免这种问题。使用volatile关键字后使得 singleton = new Singleton();
* 语句一定会按照上面拆分的步骤123来执行。
*
* 另一个问题
* 单例模式并不是绝对安全的,可以通过反射来破坏,只有枚举安全类是安全的。
*/
public class Singleton_3 {
private static Singleton_3 instance;
private Singleton_3() {
}
public static Singleton_3 getInstance() {
if (null == instance) {
synchronized (Singleton_3.class) {
if (null == instance) {
instance = new Singleton_3();
}
}
}
return instance;
}
}
4.double-check + volatile
package com.bigbear.chapter012_singleton;
/**
* @author: yxb
* @date: 2022/10/18 22:15
* @version: v1.1
* @description: double-check volatile
*/
public class Singleton_4 {
private static volatile Singleton_4 instance;
private Singleton_4() {
}
public static Singleton_4 getInstance() {
if (null == instance) {
synchronized (Singleton_4.class) {
if (null == instance) {
instance = new Singleton_4();
}
}
}
return instance;
}
}
5.枚举方式(Effective In Java推荐的方式)
package com.bigbear.chapter012_singleton;
/**
* @author: yxb
* @date: 2022/10/18 22:15
* @version: v1.1
* @description:
*/
public class Singleton_5 {
private Singleton_5() {
}
private static class InstanceHolder {
private final static Singleton_5 instance = new Singleton_5();
}
public static Singleton_5 getInstance() {
return InstanceHolder.instance;
}
}
package com.bigbear.chapter012_singleton;
/**
* @author: yxb
* @date: 2022/10/18 22:15
* @version: v1.1
* @description:
*/
public class Singleton_6 {
private Singleton_6() {
}
private enum Singleton {
INSTANCE;
private final Singleton_6 instance;
Singleton() {
instance = new Singleton_6();
}
public Singleton_6 getInstance() {
return instance;
}
}
public Singleton_6 getInstance() {
return Singleton.INSTANCE.getInstance();
}
}