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开发,但同时也在学习各种前端技术,下面是我做的一个前后端分离项目的一个小案例,不足之处请多多指教1. 项目技术选型
- 一、导入前言:导入必须用post请求具体原因在2中叙述1、Excel导入总结一下目标,就是要将excel中的数据行、逐一提取,最后得到一个l
- 说明Flutter原生是没有支持数据库操作的,它使用SQLlit插件来使应用具有使用数据库的能力。其实就是Flutter通过插件来与原生系统
- 易于理解版package com.zhebie.ternary;public class ternary { public static v
- 什么是FlutterFlutter 是谷歌推出的开发移动UI框架,可以快速的在IOS和Android上构建高质量的原生用户界面。Flutte
- C#提供了多种操作文件的方案,File类中封装的静态方法,接口封装得比较人性化,隐藏了具体实现的细节,主要包括读取、写入以及追加,这些函数如
- 先来看问题纠结了几个小时终于找到了问题所在,因为shiro的realm属于Filter,简单说就是初始化realm时,spring还未加载相
- 疑问,确实像往常一样在service上添加了注解 @Transactional,为什么查询数据库时还是发现有数据不一致的情况,想想肯定是事务
- 功能函数// 图像旋转void Rotate(const cv::Mat &srcImage, cv::Mat &dstIm
- 一、什么是IOC1)控制反转,把创建对象和对象的调用过程交给Spring 管理。2)使用IOC的目的,为了降低耦合度。二、IOC的底层原理X
- DozerDozer是一种Java Bean到Java Bean的映射器,递归地将数据从一个对象复制到另一个对象,它是一个强大的,通用的,灵
- Map的存储结构式Key/Value形式,Key 和 Value可以是普通类型,也可以是自己写的JavaBean(本文),还可以是带有泛型的
- IDEA设置Tab选项卡本人喜欢把tab选项卡全部放出来(tab选项卡默认是10个,超过后会把最先打开的挤出去,像队列一样先进先出),比如这
- 本文实例讲述了C#实现将汉字转化为2位大写的16进制Unicode的方法。分享给大家供大家参考。具体实现方法如下:说明:str.ToStri
- 本文实例讲述了C#基于委托实现多线程之间操作的方法。分享给大家供大家参考,具体如下:有的时候我们要起多个线程,更多的时候可能会有某个线程会去
- 最近完成的差不多的项目突然需要加退款的流程需求了,所以来小小的实现以下。其实对比其他的支付和退款来说,支付宝算是特别专业,也是特别简单的一个
- 下面笔者说说自己对进制转换的分析:笔者认为,任何进制都可以直接转换到十进制,而十进制也可以相当容易的转换到其他进制,所以笔者在这里将十进制作
- lambda表达式以及并行流。官方承诺你写出来的代码更运行得更快。流会自动通过Fork/Join池并行地执行。我听过一些关于Java 8的主
- Lambda表达式无法抛出异常1.Demo 例子错误提示 - Unhandled exception: java.io.IOExceptio
- 一、什么是热部署?热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。二、什么是SpringBoot热部署?SpringBoot