Java实现线程安全单例模式的五种方式的示例代码

作者:gonghr 时间:2023-09-26 16:41:23 

饿汉式

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

// 问题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:通过方法,可以提高拓展性,改进饿汉式转化为懒汉式、利用泛型特性、增加对单例对象的控制操作。

枚举单例

enum Singleton {
  INSTANCE;
}

问题1:枚举单例是如何限制实例个数的

单例相当于枚举的静态成员变量,定义几个就有几个实例。

问题2:枚举单例在创建时是否有并发问题

单例相当于枚举的静态成员变量,类加载时初始化,由 jvm 进行管理,可以保证线程安全。

问题3:枚举单例能否被反射破坏单例

不能

问题4:枚举单例能否被反序列化破坏单例

枚举实现了 Serializable 接口,可序列化,但不会被反序列破坏单例。

问题5:枚举单例属于懒汉式还是饿汉式

饿汉式

问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做

枚举允许构造方法

懒汉式

public 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 懒汉式

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,则直接返回,不用重新创建单例对象,提高了效率。

静态内部类懒汉单例

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 进行,线程安全。

来源:https://www.cnblogs.com/gonghr/p/15849395.html

标签:Java,线程安全,单例模式
0
投稿

猜你喜欢

  • Java打印出所有的水仙花数的实现代码

    2023-03-06 17:24:22
  • Mybatis下的SQL注入漏洞原理及防护方法解析

    2022-06-30 18:38:29
  • 详解利用Spring加载Properties配置文件

    2023-04-04 20:53:13
  • Android多线程学习实例详解

    2022-02-17 19:00:56
  • JVM Client和Server端有什么区别

    2023-08-05 22:49:53
  • SpringBoot整合Elasticsearch7.2.0的实现方法

    2023-11-09 19:22:56
  • Android简单获取经纬度的方法

    2021-07-28 05:26:47
  • 详解Spring MVC 集成EHCache缓存

    2022-05-28 04:42:52
  • Kotlin类与属性及构造函数的使用详解

    2021-06-04 06:04:20
  • MyBatis-Plus逻辑删除和字段自动填充的实现

    2023-01-08 03:45:54
  • 解决springboot URL带有斜杠的转义字符百分之2F导致的400错误

    2022-05-08 07:27:32
  • SpringBoot自动配置深入探究实现原理

    2023-08-06 09:59:58
  • Java面试题冲刺第五天--基础篇2

    2023-10-07 13:17:04
  • java8中:: 用法示例(JDK8双冒号用法)

    2023-11-25 06:21:21
  • spring boot项目没有mainClass如何实现打包运行

    2021-10-24 11:29:20
  • Python爬虫之爬取2020女团选秀数据

    2023-02-08 13:39:19
  • idea创建maven父子工程导致子工程无法导入父工程依赖

    2021-09-17 09:34:08
  • C#实现计算器功能(winform版)

    2021-07-16 06:40:15
  • Java Lambda表达式常用的函数式接口

    2021-10-30 13:43:53
  • Android apk 插件启动内存释放问题

    2022-05-16 07:26:39
  • asp之家 软件编程 m.aspxhome.com