ReentrantLock介绍

本文详细介绍了Java并发编程中的ReentrantLock锁,包括其公平锁与非公平锁的工作原理,以及如何正确使用lock方法来避免资源竞争和死锁。重点阐述了非公平锁的获取机制及其效率优势,同时强调了公平锁对于多线程环境下任务调度的公平性保障。最后推荐了在使用ReentrantLock时的最佳实践,确保程序的稳定性和性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java.util.concurrent.locks.ReentrantLock

 

介绍:

    reentrant 是一个排它锁,和Synchronized有相同的作用,除此之外,还提了其他功能。

 

    ReentrantLock 通过调用lock方法,使线程获取锁,多线程同时获取锁时,只有一个线程可以得到此锁,其他的线程将处于阻塞状态。如果某个线程已经获得锁,再次请求获取锁时不会被阻塞,而是锁的计数器加一,可以通过isHeldByCurrentThread或者getHoldCount两个方法得到线程是否已经持有锁。

    

    ReentrantLock 提供了公平锁和非公平锁两种机制。公平锁可以保证等待时间最长的那个线程首先被唤醒,非公平锁则随机不能保证最长时间等待的线程最先获取锁。

     

    非公平锁的大致流程如下:

         1、当前锁未被持有时,如果此时有线程调用lock需要获取锁,则立马将锁给此线程(如果AQS中的FIFO队列中有线程在等待时,多个线程可能需要去同时抢锁了)。

         2、如果已经被持有了则调用acquire走AQS的流程,但是tryAcquire中实现的算法还是一个非公平锁。

         3、nonfairTryAcquire方法的第一步是做double check,具体的就是步骤1。

     4、如果锁确实被持有了,但是此时持有锁的线程是自己本人,所以就会累加锁的计数器。

     5、如果持有锁的线程不是本人,则进入AQS的等待队列中等待去了。

     从上面的流程可以看出,非公平锁的业务流程,谁先抢先一步想获取锁,则锁就分配给谁。

    

final static class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1)) //锁未被其他线程持有时
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);//尝试获取锁。
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }



 

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {//当前可以获取锁立马
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

 

公平锁大致流程如下:

    1、线程想要获取锁,掉用lock从而执行AQS的流程。

    2、AQS首先会调用tryAcquire,这个方法内部有公平锁的所有业务流程。

    3、如果是获取锁的线程是AQS队列的第一个非Head节点节点,则得到锁,其他进入AQS的等待队列。

    AQS释放线程的顺序是从非head的第一个节点开始的,所以公平锁可以保证先获取锁的线程最先获得锁。·   

 

final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (isFirst(current) &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

 

  ReentrantLock使用lock时最好用try catch,在finally时将锁释放掉。

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...
 
   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

 

 

<think>我们被要求介绍ReentrantLock的源码和使用方法。根据引用内容,ReentrantLockJava中一个重要的锁实现,它提供了比synchronized更灵活的锁操作。我们将从以下几个方面进行介绍: 1. ReentrantLock概述 2. 核心源码解析(包括Sync、NonfairSync、FairSync) 3. 使用方法 4. 与synchronized的对比 5. 总结 注意:由于源码解析可能较长,我们将重点放在关键部分,如加锁和解锁过程。 # ReentrantLock源码解析及其使用方法 ## 一、ReentrantLock概述 `ReentrantLock`是`java.util.concurrent.locks`包下的一个可重入互斥锁,实现了`Lock`接口。它提供了与`synchronized`关键字相同的互斥性和内存可见性,但增加了可中断的锁获取、超时获取锁、公平锁等高级功能[^1]。 **核心特性**: - **可重入性**:同一个线程可以多次获得同一把锁。 - **公平性选择**:支持公平锁和非公平锁(默认非公平)。 - **锁获取的灵活性**:提供了`tryLock()`、`lockInterruptibly()`等方法。 ## 二、源码解析 ### 1. 类结构 ```java public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; // 同步器,继承自AQS abstract static class Sync extends AbstractQueuedSynchronizer { ... } static final class NonfairSync extends Sync { ... } // 非公平锁 static final class FairSync extends Sync { ... } // 公平锁 } ``` - **Sync**:继承自`AbstractQueuedSynchronizer`(AQS),是锁实现的核心。 - **NonfairSync**:非公平锁实现(默认)。 - **FairSync**:公平锁实现。 ### 2. 非公平锁加锁过程(`NonfairSync.lock()`) ```java final void lock() { if (compareAndSetState(0, 1)) // 尝试CAS快速获取锁 setExclusiveOwnerThread(Thread.currentThread()); // 成功则设置当前线程为独占线程 else acquire(1); // 进入AQS的acquire方法 } // AQS的acquire方法 public final void acquire(int arg) { if (!tryAcquire(arg) && // 再次尝试获取锁(非公平体现) acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 加入同步队列并阻塞 selfInterrupt(); } // NonfairSync的tryAcquire实现 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } // Sync中的nonfairTryAcquire final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 锁未被占用 if (compareAndSetState(0, acquires)) { // CAS尝试获取 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 重入 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } ``` **非公平性体现**:线程在请求锁时,先尝试一次CAS获取(不检查等待队列),失败后再进入AQS的同步队列排队。 ### 3. 公平锁加锁过程(`FairSync.lock()`) ```java final void lock() { acquire(1); // 直接进入AQS的acquire,不会尝试插队 } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 关键区别:检查是否有前驱节点(等待队列中是否有线程在等待) if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 重入 // ... 同非公平锁 } return false; } ``` **公平性体现**:在获取锁时,先检查同步队列中是否有等待的线程(`hasQueuedPredecessors()`),如果有则放弃获取。 ### 4. 解锁过程(`unlock()`) ```java public void unlock() { sync.release(1); // 调用AQS的release } // AQS的release public final boolean release(int arg) { if (tryRelease(arg)) { // 尝试释放锁 Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); // 唤醒后继节点 return true; } return false; } // Sync中的tryRelease protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 完全释放 free = true; setExclusiveOwnerThread(null); } setState(c); return free; } ``` **重入计数**:每次释放锁,状态值`state`减1,直到为0才完全释放。 ## 三、使用方法 ### 1. 基本使用模板 ```java ReentrantLock lock = new ReentrantLock(); // 默认非公平锁 // ReentrantLock lock = new ReentrantLock(true); // 公平锁 lock.lock(); // 阻塞获取锁 try { // 临界区代码 } finally { lock.unlock(); // 必须在finally中释放锁 } ``` ### 2. 高级用法 #### (1) 尝试获取锁(`tryLock()`) ```java if (lock.tryLock()) { // 立即返回是否成功 try { /* 操作 */ } finally { lock.unlock(); } } else { // 执行其他操作 } // 超时等待 if (lock.tryLock(1, TimeUnit.SECONDS)) { try { /* 操作 */ } finally { lock.unlock(); } } else { // 超时处理 } ``` #### (2) 可中断获取锁(`lockInterruptibly()`) ```java try { lock.lockInterruptibly(); // 可响应中断 try { /* 操作 */ } finally { lock.unlock(); } } catch (InterruptedException e) { // 中断处理 Thread.currentThread().interrupt(); // 恢复中断状态 } ``` #### (3) 条件变量(`Condition`) ```java Condition condition = lock.newCondition(); // 线程1:等待条件 lock.lock(); try { while (!conditionSatisfied) { condition.await(); // 释放锁并等待 } // 条件满足后执行 } finally { lock.unlock(); } // 线程2:唤醒 lock.lock(); try { conditionSatisfied = true; condition.signal(); // 唤醒一个等待线程 } finally { lock.unlock(); } ``` ## 四、与synchronized对比 | **特性** | **ReentrantLock** | **synchronized** | |-------------------|------------------------------------|---------------------------| | **实现方式** | Java API(AQS) | JVM关键字(monitor) | | **锁释放** | 必须手动`unlock()` | 自动释放 | | **可中断** | 支持(`lockInterruptibly()`) | 不支持 | | **公平性** | 支持(构造参数指定) | 仅非公平 | | **条件变量** | 支持多个`Condition` | 单一等待队列 | | **锁绑定** | 可跨方法释放 | 方法/代码块内 | ## 五、总结 - **优先使用场景**: - 需要高级功能(如可中断、超时、公平锁)时选择`ReentrantLock`。 - 简单同步场景仍可使用`synchronized`(代码简洁且自动释放)。 - **注意事项**: - 必须使用`try-finally`确保锁释放,避免死锁。 - 公平锁会降低吞吐量,除非必要否则使用非公平锁。 > 📖 **设计思想启示**:`ReentrantLock`通过AQS实现,将锁的获取与释放逻辑交给子类实现(模板方法模式),同时面向接口设计(`Lock`)隐藏了实现细节,使锁的使用与实现解耦[^2][^5]。 --- ### 相关问题 1. `ReentrantLock`的公平锁和非公平锁在性能上有何差异? 2. 如何通过AQS自定义实现一个锁? 3. `ReentrantLock`与`synchronized`在重入实现上有何异同? 4. 为什么`ReentrantLock`的默认实现是非公平锁?</think># ReentrantLock源码解析与使用指南 ## 一、ReentrantLock核心概述 `ReentrantLock`是Java并发包(`java.util.concurrent.locks`)中的**可重入互斥锁**实现,它提供了比`synchronized`更灵活的锁控制能力。主要特性包括: 1. **可重入性**:同一线程可多次获取同一把锁 2. **公平性选择**:支持公平锁与非公平锁(默认非公平) 3. **锁获取灵活性**:提供`tryLock()`、`lockInterruptibly()`等高级方法 4. **条件变量支持**:可通过`newCondition()`创建多个等待队列 ```java // 基本使用示例 ReentrantLock lock = new ReentrantLock(); lock.lock(); // 获取锁 try { // 临界区代码 } finally { lock.unlock(); // 必须手动释放 } ``` ## 二、核心源码解析 ### 1. 类结构体系 ```mermaid classDiagram class Lock { <<interface>> +lock() +unlock() +tryLock() } class ReentrantLock { -Sync sync +ReentrantLock() +ReentrantLock(boolean fair) } class Sync { <<abstract>> +nonfairTryAcquire() +tryRelease() } class NonfairSync { +lock() +tryAcquire() } class FairSync { +lock() +tryAcquire() } Lock <|.. ReentrantLock ReentrantLock *-- Sync Sync <|-- NonfairSync Sync <|-- FairSync ``` ### 2. 关键组件解析 #### (1) Sync同步器(继承AQS) ```java abstract static class Sync extends AbstractQueuedSynchronizer { // 非公平获取尝试 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 获取锁状态 if (c == 0) { if (compareAndSetState(0, acquires)) { // CAS尝试获取锁 setExclusiveOwnerThread(current); // 设置独占线程 return true; } } // 重入逻辑:当前线程已持有锁 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; // 增加重入计数 setState(nextc); return true; } return false; } // 释放锁实现 protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 完全释放 free = true; setExclusiveOwnerThread(null); } setState(c); return free; } } ``` #### (2) 非公平锁实现(NonfairSync) ```java static final class NonfairSync extends Sync { final void lock() { if (compareAndSetState(0, 1)) // 尝试直接获取锁(插队) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); // 进入AQS队列 } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); // 调用父类非公平获取 } } ``` #### (3) 公平锁实现(FairSync) ```java static final class FairSync extends Sync { final void lock() { acquire(1); // 直接进入队列(不插队) } protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 关键区别:检查是否有前驱节点(保证公平性) if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } // 重入逻辑与非公平锁相同 else if (current == getExclusiveOwnerThread()) { // ... } return false; } } ``` ### 3. AQS同步队列工作原理 ```mermaid graph LR A[线程尝试获取锁] --> B{锁是否空闲?} B -->|是| C[CAS获取锁] B -->|否| D[加入CLH队列尾部] D --> E[进入阻塞状态] C --> F[执行临界区代码] F --> G[释放锁] G --> H[唤醒队列中下一个线程] ``` ## 三、高级使用技巧 ### 1. 锁获取的三种方式 ```java ReentrantLock lock = new ReentrantLock(); // 1. 阻塞获取 lock.lock(); // 2. 可中断获取 try { lock.lockInterruptibly(); // 可响应中断 } catch (InterruptedException e) { // 处理中断 } // 3. 尝试获取 if (lock.tryLock()) { // 立即返回 // 获取成功 } else { // 执行备选方案 } // 带超时尝试 if (lock.tryLock(1, TimeUnit.SECONDS)) { // ... } ``` ### 2. 条件变量(Condition)使用 ```java ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 创建条件变量 // 等待线程 lock.lock(); try { while (!conditionSatisfied) { condition.await(); // 释放锁并等待 } // 条件满足后执行 } finally { lock.unlock(); } // 通知线程 lock.lock(); try { conditionSatisfied = true; condition.signalAll(); // 唤醒所有等待线程 } finally { lock.unlock(); } ``` ### 3. 公平锁与非公平锁选择 ```java // 非公平锁(默认,吞吐量高) ReentrantLock unfairLock = new ReentrantLock(); // 公平锁(减少线程饥饿,吞吐量较低) ReentrantLock fairLock = new ReentrantLock(true); ``` ## 四、性能优化与最佳实践 1. **锁释放保证**:必须将`unlock()`放在`finally`块中 ```java lock.lock(); try { // 临界区 } finally { lock.unlock(); // 确保锁释放 } ``` 2. **避免嵌套锁**:防止死锁发生 ```java // 危险示例(易死锁) public void methodA() { lock.lock(); try { methodB(); // 内部再次获取锁 } finally { lock.unlock(); } } public void methodB() { lock.lock(); // 重入安全,但设计不推荐 try { ... } finally { lock.unlock(); } } ``` 3. **锁粒度控制**:跨方法加锁示例 ```java public class ResourceManager { private final ReentrantLock lock = new ReentrantLock(); public void initResource() { lock.lock(); // ...初始化资源 // 注意:不在该方法解锁 } public void releaseResource() { try { /* 使用资源 */ } finally { lock.unlock(); } // 在另一方法释放 } } ``` ## 五、与synchronized对比 | **特性** | **ReentrantLock** | **synchronized** | |-------------------|------------------------------------|---------------------------| | **实现机制** | Java API(基于AQS) | JVM内置(monitor) | | **锁释放** | 必须手动`unlock()` | 自动释放 | | **公平性** | 支持(构造参数指定) | 仅非公平 | | **可中断性** | 支持(`lockInterruptibly()`) | 不支持 | | **条件变量** | 支持多个`Condition` | 单一等待队列 | | **性能** | 高竞争场景更优 | 低竞争场景更优 | ## 六、适用场景 1. **需要高级功能**:锁超时、可中断锁获取 2. **公平性要求**:需要按请求顺序获取锁 3. **跨方法锁控制**:在多个方法中持有和释放锁 4. **读写分离需求**:结合`ReentrantReadWriteLock`使用 5. **高竞争环境**:减少线程上下文切换开销 > **设计思想启示**:ReentrantLock通过面向接口设计(实现Lock接口)和AQS抽象,**将锁的使用与实现分离**,使开发者无需关注底层同步细节即可安全使用[^2][^5]。这种设计既保证了易用性,又提供了灵活的扩展能力。 --- ### 相关问题 1. ReentrantLock的公平锁与非公平锁在性能上有何差异? 2. 如何通过AQS实现自定义同步器? 3. Condition条件变量与Object.wait()/notify()有何异同? 4. ReentrantLock在死锁检测与恢复方面有何优势? 5. JDK中哪些并发工具基于AQS实现?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值