不使用synchronized和lock,如何实现一个线程安全的单例?

之前,在我的微信公众号(hollishcuang)上发了一条问题:不使用synchronized和lock,如何实现一个线程安全的单例?

瞬间收到了数百条回复。回答最多的是静态内部类和枚举。很好,这两种确实可以实现。

枚举

public enum Singleton {INSTANCE;public void whateverMethod() {}} 

静态内部类

public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton (){}public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}} 

还有人回答的很简单:饿汉。很好,这个也是对的。

饿汉

public class Singleton {private static Singleton instance = new Singleton();private Singleton (){}public static Singleton getInstance() {return instance;}} 

饿汉变种

public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton (){}public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}} 

(更多单例实现方式见:单例模式的七种写法

问:这几种实现单例的方式的真正的原理是什么呢?
答:以上几种实现方式,都是借助了ClassLoader的线程安全机制。

先解释清楚为什么说都是借助了ClassLoader。

从后往前说,先说两个饿汉,其实都是通过定义静态的成员变量,以保证instance可以在类初始化的时候被实例化。那为啥让instance在类初始化的时候被实例化就能保证线程安全了呢?因为类的初始化是由ClassLoader完成的,这其实就是利用了ClassLoader的线程安全机制啊。

再说静态内部类,这种方式和两种饿汉方式只有细微差别,只是做法上稍微优雅一点。这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。。。但是,原理和饿汉一样。

最后说枚举,其实,如果把枚举类进行反序列化,你会发现他也是使用了static final来修饰每一个枚举项。(详情见:深度分析Java的枚举类型—-枚举的线程安全性及序列化问题

至此,我们说清楚了,各位看官的回答都是利用了ClassLoader的线程安全机制。至于为什么ClassLoader加载类是线程安全的,这里可以先直接回答:ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。也正是因为这样, 除非被重写,这个方法默认在整个装载过程中都是同步的(线程安全的)。(详情见:深度分析Java的ClassLoader机制(源码级别)


哈哈哈哈!!!~所以呢,这里可以说,大家的回答都只答对了一半。虽然没有显示使用synchronized和lock,但是还是间接的用到了!!!!

那么,这里再问一句:不使用synchronized和lock,如何实现一个线程安全的单例?答案见:不使用synchronized和lock,如何实现一个线程安全的单例?(二)

<think>好的,我现在需要回答用户关于Java中synchronizedReentrantLock的区别,以及如何实现线程安全模式的问题。首先,我得回忆一下两者的基本概念区别。 synchronized是Java的关键字,属于内置的锁机制,而ReentrantLock是java.util.concurrent包下的一个类,需要显式地加锁解锁。记得ReentrantLock提供了更多的灵活性,比如可中断的锁获取、定时锁等待、公平性选择等,而synchronized则相对简,但可能在性能上如ReentrantLock优化后的版本。 接下来是模式的线程安全实现。常见的可能有饿汉式、懒汉式加锁、双重检查锁定等。需要确保在多线程环境下只生成一个。其中,双重检查锁定需要注意volatile关键字的使用,防止指令重排序导致的问题。另外,使用静态内部类或者枚举类的方式也是线程安全的,可能更简洁。 需要检查是否有遗漏的要点,比如锁的释放、异常处理、性能比较等。在模式部分,可能需要详细说明各种实现方式的优缺点,以及为什么某些方法更推荐。同时,要确保代码示正确,比如在双重检查锁中使用volatile,否则可能导致问题。另外,ReentrantLock中的应用可能太常见,但也可以作为一种方法,过可能如其他方式简洁。 还要注意引用用户提供的参考中的LockReentrantLock的导入语句,但用户的问题是关于synchronizedReentrantLock的区别,所以可能需要关联到这些引用,但原引用内容似乎只是一个代码片段,可能没有直接相关的信息,所以可能需要特别引用,除非在回答中提到ReentrantLock的具体用法时需要。过用户提供的引用可能太相关,所以可能引用或者仅作为锁实现的示。 最后,生成相关问题需要考虑用户可能的后续疑问,比如性能对比、适用场景、其他设计模式等。需要确保问题相关且有引导性。</think>### Java中synchronized与ReentrantLock的区别 #### 1. **实现机制** - **synchronized** 是Java语言内置的关键字,基于JVM底层实现,通过`monitorenter``monitorexit`指令实现锁的获取与释放。锁的获取释放由JVM自动管理,无需手动控制。 - **ReentrantLock** 是`java.util.concurrent.locks`包下的类,基于API实现,需显式调用`lock()``unlock()`方法,且必须通过`try-finally`块确保锁的释放[^1]。 #### 2. **功能特性** - **灵活性**: ReentrantLock支持以下特性,而synchronized支持: - **可中断锁**:通过`lockInterruptibly()`允许线程在等待锁时响应中断。 - **公平锁**:可指定锁的公平性(默认非公平),减少线程饥饿问题。 - **超时机制**:通过`tryLock(long timeout, TimeUnit unit)`设置锁等待超时。 - **条件变量**: ReentrantLock可通过`newCondition()`创建多个条件队列,精准控制线程的等待与唤醒;synchronized仅支持一条件队列。 #### 3. **性能差异** - 在低竞争场景下,synchronized因JVM优化(如偏向锁、轻量级锁)性能与ReentrantLock接近。 - 高竞争场景下,ReentrantLock通过减少上下文切换更细粒度的控制,性能更优。 #### 4. **代码示对比** ```java // synchronized实现线程安全 public synchronized void syncMethod() { // 临界区代码 } // ReentrantLock实现线程安全 private final ReentrantLock lock = new ReentrantLock(); public void lockMethod() { lock.lock(); try { // 临界区代码 } finally { lock.unlock(); } } ``` --- ### 线程安全模式实现 #### 1. **饿汉式(线程安全)** ```java public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } } ``` - **优点**:实现,无锁性能高。 - **缺点**:类加载时即初始化实,可能浪费资源。 #### 2. **双重检查锁定(Double-Checked Locking)** ```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`修饰符禁止指令重排序,避免返回未完全初始化的实。 #### 3. **静态内部类(推荐)** ```java public class Singleton { private static class Holder { static final Singleton INSTANCE = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return Holder.INSTANCE; } } ``` - **原理**:利用类加载机制保证线程安全,延迟初始化且无需同步。 #### 4. **枚举类(最佳实践)** ```java public enum Singleton { INSTANCE; // 其他方法 } ``` - **优点**:简洁、线程安全、自动防反射攻击,推荐作为的标准实现。 --- ### 对比总结 | 特性 | synchronized | ReentrantLock | |---------------------|----------------------------------|-----------------------------| | **锁释放** | 自动释放(代码块/方法结束) | 手动调用`unlock()` | | **公平性** | 仅支持非公平锁 | 支持公平与非公平锁 | | **锁中断** | 支持 | 支持可中断锁 | | **条件变量** | 一条件队列 | 多条件队列 | | **适用场景** | 简同步需求 | 复杂并发控制 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值