RenntrantLock 理解

本文深入解析ReentrantLock的实现原理及应用,介绍其作为可重入锁的特点,并基于AQS框架实现公平锁与非公平锁的区别。通过了解ReentrantLock的工作机制,包括线程获取锁的过程及状态检查机制,帮助读者掌握高级并发控制技术。

1.ReentrantLock 是可重入锁。

可重入的意思是在线程已经获取某个共享资源的锁之后,释放锁之前 还可以再次对于这个共享资源获取锁。

2.ReentrantLock  是基于AQS实现的。分为公平锁和非公平锁。

AQS 是一种定义了多线程访问共享资源的同步器框架。AQS并不实现任何功能,底层是维护了一个CLH 虚拟的双向链表,其中节点是线程。

AQS原理文章:https://www.cnblogs.com/waterystone/p/4920797.html 

3.ReentrantLock原理

当一个线程获想要取到一个共享资源的锁时,先看该资源的status值是否为0(0说明该资源未被加锁,大于0说明被其他线程加锁了)如果为0,获取该资源的锁,把status的值设为1;如果大于0,把该线程加入虚拟的双向链表中的尾节点。同时使用CAS一直询问共享资源的status是否为0。如果为0,判断当前节点是否为头节点,如果是头节点,获取锁,如果不是,继续等待。

学习文章:https://www.cnblogs.com/xrq730/p/4979021.html

<think>首先,用户的问题是关于synchronized和ReentrantLock在Java中的区别。用户明确表示想要了解“各方面的区别”,所以我需要提供一个全面的比较。参考了提供的引用内容,我可以总结出几个关键区别点。 回顾提供的引用: -[^1]:提到了5个主要区别:用法不同(synchronized修饰方法或代码块 vs ReentrantLock只用于代码块)、获取/释放机制(自动 vs 手动)、锁类型(非公平 vs 可选择公平/非公平)、响应中断(ReentrantLock支持,synchronized不支持)、底层实现(JVM监视器 vs AQS)。 -[^2]:补充了性能优化(synchronized在JDK1.6后性能接近ReentrantLock),总结了synchronized简洁自动、适合简单场景;ReentrantLock功能强大(支持中断、公平锁、定时获取、多条件变量),但需手动释放。 -[^3]:强调了在使用ReentrantLock时,必须在finally块中释放锁以避免死锁。 -[^4]:重复了用法便利性(synchronized更简洁)和ReentrantLock需要手工释放锁。 用户的上一个问题或上下文:之前的对话是关于系统级指令的示例,用户这次是直接询问区别,没有历史回答参考。所以,我需要基于当前引用构建回答。 现在,我需要确保回答符合系统级指令: - 所有行内数学表达式使用$...$(但在这个问题中可能不需要数学表达式)。 - 独立公式使用$$...$$并单独成段(同样,这里可能不涉及)。 - LaTeX语法正确(确保代码块正确)。 - 使用中文回答。 - 生成相关问题(在回答结尾添加相关问题)。 - 在引用的段落末尾添加引用标识,如[^1]。 结构化回答: 1. **引言**:简要介绍两者的共同点(都是可重入锁)。 2. **主体**:列出各方面的区别,基于引用内容组织。确保覆盖: - 用法 - 获取和释放机制 - 锁类型(公平性) - 响应中断 - 底层实现 - 性能 - 功能扩展 - 使用便利性 3. **代码示例**:添加一个代码示例来演示两者的使用,如引用[^3]中提到的。用代码块展示。 4. **总结**:概括何时使用哪一个。 5. **相关问题**:在结尾添加相关问题,如系统指令所示。 添加引用:在引用的段落末尾自然地添加[^1]、[^2]等。由于引用来自提供的文本,我需要对应: - 引用[^1]:描述基本区别 - 引用[^2]:性能优化和总结 - 引用[^3]:代码示例和finally块的重要性 - 引用[^4]:便利性 确保引用标识正确放置。 相关问题生成: - 基于主题,如: 1. 在哪些场景下优先使用synchronized? 2. 如何优化ReentrantLock的使用? 3. synchronized的性能优化细节是什么? 4. 可重入锁的其他实现? 5. 如何处理死锁问题? 最终回答用中文。 </think>### synchronized 和 ReentrantLock 在 Java 中的全面比较 在 Java 并发编程中,`synchronized` 和 `ReentrantLock` 都是实现线程同步的可重入锁(Reentrant Lock),即同一个线程可以多次获得同一把锁而不会死锁。尽管它们在功能上相似,但在多个方面存在显著差异。以下基于引用内容,从用法、机制、性能等角度进行详细分析。为了直观展示,我会在必要时添加代码示例。 #### 1. **用法差异** - **synchronized**:可以作为关键字修饰普通方法、静态方法或代码块。使用更简洁,编译器会自动处理锁的获取和释放。例如: ```java public class SynchronizedCounter { private int count = 0; public synchronized void increment() { count++; } } ``` 这里,`synchronized` 直接修饰方法,确保线程安全[^1][^4]。 - **ReentrantLock**:是一个类,只能用于代码块中,需要显式创建锁对象。使用更灵活,但代码量更大: ```java import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockCounter { private int count = 0; private final ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); // 必须手动释放,避免死锁 } } } ``` 注意:`ReentrantLock` 要求在 `finally` 块中释放锁,以防止异常导致锁泄露[^3][^4]。 #### 2. **获取和释放机制** - **synchronized**:锁的获取和释放是自动的。当线程进入 `synchronized` 块或方法时,JVM 自动加锁;退出时自动释放锁,无需手动干预。这使得代码更可靠,但灵活性较低[^1][^4]。 - **ReentrantLock**:需要手动调用 `lock()` 和 `unlock()` 方法。这提供了更精细的控制,但也增加了风险——如果忘记调用 `unlock()`,可能导致死锁。因此,必须在 `finally` 块中释放锁[^3][^4]。 #### 3. **锁类型(公平性)** - **synchronized**:始终是非公平锁(Non-fair Lock)。这意味着锁的获取顺序不保证“先到先得”,可能导致线程饥饿(某些线程长时间等待)[^1]。 - **ReentrantLock**:默认为非公平锁,但可以通过构造函数参数指定为公平锁(Fair Lock)。公平锁会按线程请求顺序分配锁,减少饥饿问题: ```java ReentrantLock fairLock = new ReentrantLock(true); // 设置为公平锁 ``` 这在需要严格顺序的场景(如任务调度)中更有优势[^1]。 #### 4. **响应中断能力** - **synchronized**:不支持响应中断。如果一个线程在等待锁时被中断(调用 `interrupt()`),它不会退出等待状态,可能导致死锁难以解决[^1]。 - **ReentrantLock**:支持响应中断。线程可以通过 `lockInterruptibly()` 方法获取锁,如果等待中被中断,会抛出 `InterruptedException`: ```java try { lock.lockInterruptibly(); // 临界区代码 } catch (InterruptedException e) { // 处理中断 } finally { lock.unlock(); } ``` 这在高并发系统中能有效避免死锁[^1][^2]。 #### 5. **底层实现** - **synchronized**:基于 JVM 层面的监视器(Monitor)实现。每个 Java 对象都有一个内置锁(Monitor Lock),通过字节码指令(如 `monitorenter` 和 `monitorexit`)管理。这使其执行高效,但扩展性受限[^1][^2]。 - **ReentrantLock**:基于 AQS(AbstractQueuedSynchronizer)框架实现。AQS 提供了队列机制来管理线程阻塞,支持更复杂的同步逻辑(如条件变量)。这使得 `ReentrantLock` 功能更强大,但实现较复杂[^1][^2]。 #### 6. **性能对比** - **历史优化**:在 JDK 1.6 之前,`synchronized` 性能较差,但在 JDK 1.6 及后续版本中,JVM 引入了锁优化(如偏向锁、轻量级锁、适应性自旋等),使其在常见场景下的性能接近 `ReentrantLock`[^2]。 - **实际测试**:在低竞争场景(如少量线程),`synchronized` 通常更快(因为 JVM 优化);在高竞争场景(如数千线程),`ReentrantLock` 可能更优(因为精细控制)。但整体差异不大,优先考虑代码简洁性[^2]。 #### 7. **功能扩展** - **synchronized**:功能较基础,仅支持基本的互斥访问。不支持条件变量或定时获取锁。 - **ReentrantLock**:提供高级功能: - **条件变量(Condition)**:允许线程在不同的条件下等待和唤醒。 ```java private final Condition condition = lock.newCondition(); ``` - **定时获取锁**:通过 `tryLock(long timeout, TimeUnit unit)` 设置超时时间。 - **锁状态查询**:可用 `isLocked()` 或 `getQueueLength()` 监控锁状态。 这些功能适用于复杂同步逻辑(如线程池管理)[^2][^3]。 #### 总结 - **优先使用 synchronized**:当同步逻辑简单时(如单方法同步),`synchronized` 更简洁、安全(自动释放锁),且性能优化后足够高效[^2][^4]。 - **优先使用 ReentrantLock**:当需要高级功能(如公平锁、中断响应、条件变量)或在高并发系统中时(如电商秒杀),`ReentrantLock` 提供更精细的控制[^1][^2]。 在实际应用中,90% 以上的场景可用 `synchronized` 解决;特殊需求下,`ReentrantLock` 是强大补充。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值