Java并发编程(二)线程安全的单例模式

一、双重检查锁定(Double-Checked Locking)

        这种设计模式的目的是为了减少在多线程环境下获取锁的开销,尤其是当实例化对象的操作很昂贵,且该对象只会被实例化一次时。双重检查锁定模式的基本思想是,在创建对象之前进行两次检查:首先,无锁地检查实例是否已经创建;如果未创建,才进行加锁,再次检查实例是否已创建,如果仍未创建,则在同步块内实例化对象。
然而,在早期的Java内存模型(JMM)中,这段看似高效的代码存在一些问题,主要体现在以下几点:
        1. 指令重排序:编译器或处理器为了优化性能,可能会对代码执行顺序进行重新排序。在双重检查锁定模式中,即使实例化的代码在同步块内,对象的字段初始化和对象引用的赋值操作仍可能被重排序。这意味着其他线程有可能在对象完全构造完成前就看到对象引用,从而访问到一个部分构造的对象,导致不可预料的行为。
        2. 可见性问题:没有使用volatile关键字修饰实例变量时,新创建的对象实例可能不会立即对其他线程可见,导致其他线程看到的是旧的值(即null),即使对象已经被正确初始化。
为了解决这些问题,通常采取以下措施:
        3. 使用volatile关键字:确保对实例变量的写操作之前的所有操作都先行发生,且该写操作之后的所有读操作都能看到这个写的结果。这解决了指令重排序和可见性问题。
修正后的双重检查锁定模式示例代码如下:

/*** Singleton类的实例化方法。保证线程安全的单例模式实现。* 采用双重检查锁定的方式,避免多次实例化,保证线程安全。* * @return 返回Singleton类的唯一实例。*/
public static Singleton getInstance() {// 如果实例尚未创建,则进入同步块if(instance ==null){synchronized(Singleton.class){// 在同步块内部再次检查实例是否存在,避免不必要的锁定if(instance == null){instance = new Singleton();}}}return instance;
}

这段代码中包含了一个静态的、volatile修饰的Singleton实例,这是为了确保在多线程环境下,实例的创建是线程安全的。使用双重检查锁定的方式,在实例未被创建时对访问进行同步,以确保只有一个线程能够创建实例。这种方式既避免了线程安全问题,又尽可能地降低了同步的开销。

尽管使用volatile和双重检查锁定可以有效解决上述问题,但在现代Java实践中,推荐使用基于类加载机制的静态内部类方式来实现单例模式,因为这种方式既简洁又避免了双重检查锁定可能带来的复杂性。 

二、 基于类加载机制的静态内部类单例模式

基于类加载机制的静态内部类单例模式是一种非常简洁且线程安全的方式,它利用了Java类加载机制来确保单例的唯一性。以下是该模式的示例代码:

/*** 一个使用静态内部类实现的单例类。这种实现方式保证了线程安全,并且在类加载时才初始化单例,* 实现了延迟初始化(lazy initialization)。*/
public class Singleton {/*** 私有构造函数,防止外部直接实例化Singleton类。*/private Singleton() {}/*** 静态内部类SingletonHolder,用于持有Singleton的唯一实例。这种方式确保Singleton实例在需要时才被创建,* 且由于类加载机制,其创建过程是线程安全的。*/private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}/*** 获取Singleton类的唯一实例。调用此方法将返回Singleton的实例,首次调用时实例才会被创建。** @return Singleton类的唯一实例。*/public static Singleton getInstance() {// 直接返回SingletonHolder类中持有的Singleton实例,实现延迟初始化和线程安全return SingletonHolder.INSTANCE;}
}

在这个例子中,Singleton 类包含一个私有的静态内部类 SingletonHolder。SingletonHolder 类中有一个静态常量 INSTANCE,用于持有 Singleton 的单例实例。由于静态内部类只有在外部类首次被加载时才会被加载,而类加载是线程安全的,因此 Singleton 的实例化过程也是线程安全的。
当调用 Singleton.getInstance() 方法时,会直接返回 SingletonHolder.INSTANCE,而不需要进行任何同步操作。这样既保证了单例的线程安全性,又避免了使用 synchronized 或 ReentrantLock 带来的额外开销。

这种方式不仅保证了线程安全性,而且通过延迟初始化(lazy initialization)提高了效率,因为Singleton实例只有在第一次调用getInstance()时才会被创建。同时,由于静态内部类的特性,它的加载是在第一次被引用时才会发生,因此避免了类加载时就初始化单例的问题。这种实现方式的优点在于:
线程安全:由于类加载过程是线程安全的,所以无需担心并发问题。
延迟初始化:单例实例只在 getInstance 被第一次调用时创建,实现了延迟初始化。
代码简洁:没有使用锁或其他同步机制,代码更简洁易懂。
请注意,这种方式适用于不需要在类加载时就初始化单例的场景。如果单例需要在类加载时就初始化,那么应选择其他实现方式,例如枚举单例。

三、枚举单例

枚举单例是Java中实现单例模式的一种优雅且线程安全的方式。这种方式利用了Java枚举的特性,使得单例在类加载时自动初始化,而且无法通过反射或其他方式破坏其唯一性。以下是一个简单的枚举单例模式的示例:

public enum Singleton {INSTANCE;// 可以添加方法和其他属性private String someProperty = "Some value";public String getSomeProperty() {return someProperty;}public void setSomeProperty(String value) {this.someProperty = value;}
}

 在这个例子中,Singleton 是一个枚举类型,只有一个元素 INSTANCE。这个元素就是单例的实例。由于枚举在Java中是唯一的,不能被实例化多次,因此保证了单例的唯一性。同时,枚举的初始化是在类加载时完成的,因此是线程安全的
要使用这个单例,只需调用 Singleton.INSTANCE,就像访问枚举常量一样:

Singleton singleton = Singleton.INSTANCE;
System.out.println(singleton.getSomeProperty());

枚举单例的优点包括:
线程安全:Java保证了枚举实例的创建是线程安全的。
懒加载:尽管枚举在类加载时初始化,但由于枚举的静态特性,实际上只在第一次访问时才会触发枚举实例的创建。
防止反射攻击:枚举实例不能通过反射创建,增强了单例的安全性。
简单明了:代码简洁,易于理解和维护。
这种实现方式是Joshua Bloch在《Effective Java》一书中推荐的单例实现方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/842848.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记——交通安全分析03

目录 前言 当天学习笔记整理 绪论 交通行为、心理与安全 结束语 前言 #随着上一轮SPSS学习完成之后,本人又开始了新教材《交通安全分析》的学习 #整理过程不易,喜欢UP就点个免费的关注趴 当天学习笔记整理 绪论 针对道路设施安全,我…

铜镁合金应用领域广泛 我国具备原材料优势

铜镁合金应用领域广泛 我国具备原材料优势 铜镁合金又称铝青铜,是一种变形铝合金,指将镁金属、铝金属和铜金属经合金化反应制成的合金。与普通铝合金相比,铜镁合金具有比强度高、耐磨性好、减振性能佳、轻量化等优势,在金属冶炼、…

【前端】XML和HTML的区别详解

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…

猫耳 WebSocket 跨端优化实践

前言 在现代的移动应用程序中,长连接是一种不可或缺的能力,包括但不限于推送、实时通信、信令控制等常见场景。在猫耳FM的直播业务中,我们同样使用了 WebSocket 长连接作为我们实时通信的基础。 在我们推进用户体验优化的工作中,…

IC开发——Ubuntu安装VCS2018

1. 简介 VCS是一种常用的Verilog仿真和综合工具,由Synopsys公司开发。它提供了一个完整的设计验证环境,用于验证硬件设计的正确性和性能。以下是VCS工具的一些主要特点和功能: 仿真功能:VCS支持基于事件驱动的数字电路级仿真&am…

【Linux命令】--- Linux下的分卷压缩与解压

在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。 【Linux命令】--- 多核压缩命令大全&…

vue通过数据劫持可以精准的探测数据变化,为什么还要进行diff检测差异?

Vue通过数据劫持(也称为响应式系统)确实可以精准地探测到数据的变化,但为什么还要进行Diff检测差异呢?这主要是出于以下原因: 核心是最小化dom更新 性能优化: 数据劫持虽然可以侦测到数据的变化&#xff0c…

markdown画时序图的时候,如何自动显示每一条时序的序号

1: 现象描述 今天画时序图的时候,发现时序上面没有显示序号,看起来不够清晰,只有单纯的说明; 如下图所示 刚测试CSDN的时序图,默认是带序号的,看起来和实际使用的markdown工具有关系; 2:解决办…

XPosed项目的接入、模版制作、改名全过程

XPosed项目的接入、模版制作、改名全过程 写在前面 之前写过这篇Xposed Hook 过登录密码验证配置开发Xposed项目的文章,这次的接入使用的是当前最新版Android Studio,接入稍微有些差别,也记录下。 本篇文章主要是写关于XP项目接入、制作XP模…

两年前的微信聊天记录能恢复吗?正确答案在这里(全)

微信已经成为我们日常沟通中不可或缺的一部分,承载着无数重要的对话和回忆。然而,面对手机更换、系统升级或意外删除等情况,许多人不禁要问:两年前的微信聊天记录能恢复吗?这个问题的答案并不简单,因为能否…

WGCLOUD部署好后,怎么登录WGCLOUD界面

WGCLOUD的server启动完成后,我们在浏览器里输入URL,如下 http://[server主机IP]:9999 注意默认端口就是9999,如果修改过,那么把端口改成自己的实际端口 这样就可以看到登录页面了,默认账号密码是:admin/…

辅助科技照亮道路,携手共促盲文书写技能新飞跃

在这个科技日新月异的时代,创新的力量正以前所未有的方式融入我们的日常生活,特别是对于视觉障碍群体而言,技术的每一次进步都是通往更加独立生活的桥梁。今天,让我们聚焦于一款名为“蝙蝠避障”的辅助软件,它不仅为盲…

Python中日期和时间的互相转换指南

在Python开发中,处理日期和时间是一种非常常见的需求。不论是在数据处理、日志管理、用户行为分析还是其他场景中,我们经常需要在时间戳和日期对象之间相互转换,以及处理相对时间计算。今天,我将通过一个实用的例子向大家展示如何…

探索数字规律与数组操作

新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一、问题描述与需求概述 二、数字规律的理解 1. 观察数字模式 2. 思考生成方法 三、实现…

C/C++利用 %f 格式化输出整型变量的结果为0.000000

【算法代码】 #include <bits/stdc.h> using namespace std;int main() {int a97;printf("%f\n",a); //0.000000printf("%f\n",(float)a); //97.000000 }/* out: 0.000000 97.000000 */ 【算法分析】 ● 强制类型转换&#xff08;1&#xff09;强制…

Ubuntu24.04安装tabby-terminal-1.0.207并处理依赖

1 下载 tabby-terminal-1.0.207 地址&#xff1a; https://github.com/Eugeny/tabby/releases 点击show all 36 assets 选择 tabby-1.0.207-linux-x64.deb 并下载。 2 依赖下载 gconf2_3.2.6-3ubuntu6_amd64.deb gconf2-common_3.2.6-3ubuntu6_all.deb gconf-service_3.2.6-…

嵌入式开发中做文件同步时的常用工具

在嵌入式开发中&#xff0c;文件同步工具是至关重要的&#xff0c;它们可以帮助开发者在本地计算机和嵌入式设备之间高效地同步文件。根据调研结果&#xff0c;常用嵌入式开发中的文件同步工具有四种&#xff1a; 1.rsync&#xff1a;这是一个非常流行的文件同步工具&#xff…

Vue实现列表的无缝滚动功能

Vue实现列表的无缝滚动功能 直接上代码 codePen地址 <template><div id"app"><div class"list" mouseenter"mouseEnter" mouseleave"mouseLeave"><divclass"list-box"ref"listRef":style&…

怎么挑选骨传导耳机?精选六大选购技巧教你如何挑选

过去的两年里&#xff0c;骨传导耳机逐渐被大众的所熟知。可能毕竟长时间使用音量过大的传统入耳式耳机&#xff0c;多多少少会对我们的听力健康构成威胁。所以很多人就想找一款不伤耳朵的耳机。然后就了解到了骨传导耳机&#xff0c;所以就会延伸出这些问题——骨传导耳机好用…

vue3学习(二)

前言 上一篇分享了vue的基础指令&#xff0c;这篇记录下vue3的核心内容&#xff0c;也是自己的学习笔记&#xff0c;可能有些核心还不全&#xff0c;大佬请略过。 一、核心内容 分享这个之前&#xff0c;先声明下&#xff0c;我这里是用的脚手架的写法&#xff0c;分享的讲解截…