【设计模式】实现线程安全单例模式的五种方式

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475

饿汉式

饿汉式:类加载就会导致该单实例对象被创建

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

java`// 问题1:为什么加 final
// 问题2:如果实现了序列化接口, 还要做什么来防止反序列化破坏单例
public final class Singleton_hungry implements Serializable {

// 问题3:为什么设置为私有? 是否能防止反射创建新的实例?
private Singleton_hungry(){}

// 问题4:这样初始化是否能保证单例对象创建时的线程安全?
private static Singleton_hungry INSTANCE = new Singleton_hungry();

// 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由
public static Singleton_hungry getInstance() {
return INSTANCE;
}
public Object readResolve(){ // 防止反射创建新的实例?
return INSTANCE;
}
}`

  • 问题1:
    避免子类覆盖父类的一些方法,导致线程不安全。
  • 问题2:
    实现 readResolve 方法。当从对象流 ObjectInputStream 中读取对象时,会检查对象的类否定义了 readResolve 方法。如果定义了,则调用它返回我们想指定的对象(这里就指定了返回单例对象)。
  • 问题3:防止通过 new 创建对象实例。不能防止反射创建新的实例。
  • 问题4:可以。静态变量初始化在类加载时进行,由 jvm 进行管理,可以保证线程安全。
  • 问题5:通过方法,可以提高拓展性,改进饿汉式转化为懒汉式、利用泛型特性、增加对单例对象的控制操作。

枚举单例

复制代码
  • 1
  • 2
  • 3

javaenum Singleton { INSTANCE; }

  • 问题1:枚举单例是如何限制实例个数的
    单例相当于枚举的静态成员变量,定义几个就有几个实例。
  • 问题2:枚举单例在创建时是否有并发问题
    单例相当于枚举的静态成员变量,类加载时初始化,由 jvm 进行管理,可以保证线程安全。
  • 问题3:枚举单例能否被反射破坏单例
    不能
  • 问题4:枚举单例能否被反序列化破坏单例
    枚举实现了 Serializable 接口,可序列化,但不会被反序列破坏单例。
  • 问题5:枚举单例属于懒汉式还是饿汉式
    饿汉式
  • 问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做
    枚举允许构造方法

懒汉式

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

typescriptpublic final class Singleton\_lazy { private Singleton\_lazy(){} private static Singleton_lazy INSTANCE = null; // 缺点 public static synchronized Singleton_lazy getInstance() { if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_lazy(); return INSTANCE; } }

  • synchronized 保证线程安全,但锁粒度较大,性能低。

DCL 懒汉式

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

java`public final class Singleton_DCL {

private Singleton_DCL() {}

// 问题1:解释为什么要加 volatile ?
private static volatile Singleton_DCL INSTANCE= null;

// 问题2:对比实现3, 说出这样做的意义
public static Singleton_DCL getInstance() {
if(INSTANCE != null) {
return INSTANCE;
}
synchronized (Singleton_DCL.class) {

// 问题3:为什么还要在这里加为空判断, 之前不是判断过了吗
if(INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new Singleton_DCL();
return INSTANCE;
}
}
}`

  • 问题1:避免指令重排序,导致赋值语句先于构造函数执行,得到一个未初始化完毕的对象。
  • 问题2、3:Double Check Lock 机制。同步代码块外部的判断语句主要用于 INSTANCE 初始化并赋值之后,此时 INSTANCE != null,如果有多个线程尝试获取单例,可以提前返回,不用执行同步代码块。而同步代码块内部的判断主要用于第一次初始化时,INSTANCE = null,此时可以有多个线程尝试获取 INSTANCE,只能有一个线程进入同步代码块,其他线程在同步代码块外阻塞,该线程创建一个单例对象之后,唤醒其他线程,再进入同步代码块,发现 INSTANCE != null,则直接返回,不用重新创建单例对象,提高了效率。

静态内部类懒汉单例

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

java`public final class Singleton_LazyHolder {
private Singleton_LazyHolder(){}

// 问题1:属于懒汉式还是饿汉式
private static class LazyHolder{
static final Singleton_LazyHolder INSTANCE = new Singleton_LazyHolder();
}

// 问题2:在创建时是否有并发问题
public static Singleton_LazyHolder getInstance() {
return LazyHolder.INSTANCE;
}
}`

  • 问题1:懒汉式。静态内部类只有在被方法调用的时候才进行初始化,类加载。
  • 问题2:无,类加载由 jvm 进行,线程安全。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要手写一个线程安全单例模式,可以使用懒汉式和双重检查锁定的方式实现。懒汉式是指在需要使用单例对象时才去创建它,双重检查锁定则是为了保证在多线程环境下只有一个线程能够创建该对象。 以下是一个示例代码: ```java public class Singleton { private static volatile Singleton instance; private Singleton() { // 私有构造方法 } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` 在这个示例中,我们使用了volatile关键字来确保instance变量在多线程环境下的可见性。在getInstance()方法中,首先检查instance是否为null,如果为null,则进入同步块。在同步块内部再次检查instance是否为null,这是为了防止多个线程同时通过了第一个检查,进入同步块后再创建多个实例的情况。如果instance仍然为null,才会创建一个新的Singleton实例并赋值给instance变量。 需要注意的是,双重检查锁定方式只在Java 5及以上版本才能保证正确性,因为在Java 5之前的版本中,volatile关键字的语义不够强大,可能会导致创建出多个实例。而在Java 5及以上版本中,通过volatile关键字能够确保instance变量的可见性,并且禁止指令重排序,从而保证线程安全。 以上是一种手写线程安全单例模式实现方式,通过使用双重检查锁定和volatile关键字,能够在多线程环境下保证只有一个实例被创建。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java--设计模式单例模式+保证线程安全的5种写法(懒、汉、内、双、枚)](https://blog.csdn.net/JustinQin/article/details/120668592)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值