java用sychronized实现的单例模式两种实现方式有什么区别?

二哥的博客 二哥的博客 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区
看设计模式的时候,网上有代码说,在考虑多线程的时候,用sychronized做同步的时候必须将getInstance和new操作分开,否则会有可能出错;也就是讲下面第一段代码改为第二段:

public class SingletonTest {

private static SingletonTest instance = null;

private SingletonTest() {
}


public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
​​ }
​
}


改成:

public class SingletonTest {


private static SingletonTest instance = null;

private SingletonTest() {
}

private static synchronized void syncInit() {
if (instance == null) {
instance = new SingletonTest();
}
}

public static SingletonTest getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
}

​

网上的理由是:
在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

a>A、B线程同时进入了第一个if判断

b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();

c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。

d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。

e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。



看了很不理解,改进的方法不是也有同样的问题? 两者的区别在哪儿?(对面同步就能保证instance一定是实例化之后才被使用么?)
...全文

相关推荐

JAVA中常用的数据结构

JAVA中常用的数据结构,适用参加工作初期的Java开发者。

qq_39292819

java并发编程面试题分享给需要的同学.docx

并发编程 并发编程夺命连环问 JUC 什么是死锁?如何避免死锁? 什么是可重入锁? CountDownLatch CyclicBarrier Semaphore 什么是自旋锁(CAS,compare and swap)? CAS存在的问题 什么是读写锁? 谈谈并发编程三要素 简述Java内存模型(JMM) volatile关键字知道么,它是怎么实现的?(难点 重要) sychronized和Lock(ReentrantLock)之间的区别? sychoronized的自旋锁、偏向锁、轻量级锁、重量级锁、分别介绍和联系 threadLocal会发生内存泄漏吗?谈谈你对threadLocal的理解 为什么会发生内存泄漏? 对threadLocal的理解 并发、并行、串行的区别? 如何查看线程死锁 1. linux服务器 2. MySQL中的思索可以通过语句查询判断,如下 线程&线程池 为什么用线程池?解释下线程池参数? 你们项目中,线程池是怎么用的(配置的)? CPU密集型 IO密集型 线程池中阻塞队列的作用是什么?为什么是先添加队列而不是先创建最大线程? 谈谈线程的生命周期?线程有哪些状

飞翔的佩奇

leetcode下载-study:学习笔记

leetcode下载 目录 操作系统 虚拟内存与物理内存转化 CPU 调度方式 进程栈大小 进程与线程区别 线程状态 网络 TCP 四次挥手 SYN Flood 如何避免 HTTP 头部字段 HTTP 请求方式 AIO OSI 七层模型 ARP 协议 Mysql Mysql 数据存储原理 Mysql 索引 abc 复合索引 数据库隔离级别 InnoDB 与 MySAIM 区别 Mysql MVCC JVM Java 类加载过程 Java 类加载机制 新生代频繁 gc 如何调整 CMS 垃圾回收器 锁 Lock 与 Sychronized 区别 Redis 分布式锁 Zookeeper 分布式锁 死锁产生原因 如何解决死锁 数据库死锁例子 JAVA 并发 AQS 源码 ReentrantLock 源码 Condition 源码 ConcurrentHashMap 源码 Java 线程状态 JAVA 数据结构 Queue HashMap HashTable ConcurrentHashMap JAVA IO Go Redis Redis Cluster Redis 持久化方式 Redis

抹蜜茶

Sychronized和ReentrantLock的区别

sychronized锁的是对象,锁信息保存在对象头中,ReentrantLock通过代码中int类型的state标识来标识锁的状态。sychronized会⾃动的加锁与释放锁,ReentrantLock需要程序员⼿动加锁与释放锁。sychronized的底层是JVM层⾯的锁,ReentrantLock是API层⾯的锁。sychronized是⾮公平锁,ReentrantLock可以选择公平锁或⾮公平锁。锁的状态(偏向锁、轻量级锁、重量级锁)由 JVM 自动管理。默认是非公平锁(不保证线程获取锁的顺序)。

EthanMilk

通信行业基于MongoDB的安全合规与性能优化:5G时序数据处理、可查询加密及窗口函数应用

内容概要:本文围绕MongoDB在通信行业的深度应用,系统讲解了可查询加密、时间序列集合、窗口函数与性能诊断四大核心技术。通过详实的代码案例,展示了如何利用可查询加密实现敏感数据合规存储与查询,使用时序集合高效处理5G网络KPI等高频写入场景,借助窗口函数进行经营指标的趋势分析,并结合explain()与查询分析器完成性能调优。文章还涵盖分片一致性检查、索引优化等运维实践,全面覆盖通信系统对安全、性能与扩展性的严苛要求。; 适合人群:具备一定数据库基础,从事通信行业研发、架构或运维工作1-3年的技术人员,尤其是参与5G网络监控、客户数据管理或大数据平台建设的相关人员。; 使用场景及目标:① 实现用户手机号、身份证等敏感信息的加密存储与等值查询,满足安全合规要求;② 构建高吞吐、低延迟的5G基站KPI时序数据存储与分析系统;③ 在数据库层直接完成滑动平均、累计求和等经营分析,减少应用层计算负担;④ 诊断慢查询、验证索引有效性,保障系统高性能运行。; 阅读建议:此资源强调理论与实战结合,建议读者在本地或测试环境部署MongoDB 7.0+版本,动手复现文中代码,重点关注schemaMap配置、时序集合参数选择与窗口函数语法细节,并结合explain执行计划理解底层优化机制,从而真正掌握通信级数据架构的设计精髓。

数智顾问
评论 9
收藏 已收藏
分享
返回顶部
二哥的博客
Java 51,407 成员 86,129 内容