ReentrantLock 可重入的独占锁

ReentrantLock是Java中可重入的独占锁,支持公平锁和非公平锁选择。它允许一个线程多次获取同一锁,确保线程安全。在释放锁时,会递减获取锁的计数,直到计数为0才真正释放。公平锁遵循FIFO原则,而非公平锁不保证获取顺序,可能造成线程饥饿,但能提高系统吞吐量。
摘要由CSDN通过智能技术生成

参考:Java并发编程的艺术

JDK版本AdoptOpenJDK 11.0.2+9

ReentrantLock 是可重入的独占锁,特征如下:

  • 只有一个线程可以获取锁。
  • 支持一个线程对共享资源的重复加锁。
  • 支持获取锁时候的公平性和非公平性选择。

1 可重入

可重入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞。

ReentrantLock实现了可重入锁的逻辑,解决两个关键问题:

  1. 支持线程再次获取锁。 锁需要识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取锁。
  2. 锁的最终释放。 通过计数器记录锁被重复获取的次数,而锁被释时,计数自减,当计数等于0时表示锁成功释放。

1.1 可重入获取锁

ReentrantLock在公平锁和非公平锁(默认实现)的实现中都实现了锁的可重入性。

以非公平锁为例,源码如下:

    @ReservedStackAccess
    final boolean nonfairTryAcquire(int acquires) {
   
        // 获取当前线程
        final Thread current = Thread.currentThread();
        // 获取同步状态的值
        int c = getState();
        // 如果还没有线程获取同步状态,c就等于0
        if (c == 0) {
   
            // 设置同步状态的值
            if (compareAndSetState(0, acquires)) {
   
                // 如果成功,设置独占模式下获得锁的是哪个线程
                setExclusiveOwnerThread(current);
                // 返回true,表示成功获取了锁
                return true;
            }
        }
        // 如果当前线程就是获取了锁的线程,重入
        else if (current == getExclusiveOwnerThread()) {
   
            // 计算同步状态要更新的值
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            // 设置同步状态的值
       
重入(ReentrantLock)是一种独占,也就是说同一时间只能有一个线程持有该。与 synchronized 关键字不同的是,重入可以支持公平和非公平两种模式,而 synchronized 关键字只支持非公平重入实现原理是基于 AQS(AbstractQueuedSynchronizer)框架,利用了 CAS(Compare And Swap)操作和 volatile 关键字。 重入的核心思想是“可重入性”,也就是说如果当前线程已经持有了该,那么它可以重复地获取该而不会被阻塞。在重入内部,使用了一个计数器来记录当前线程持有该的次数。每当该线程获取一次时,计数器就加 1,释放一次时,计数器就减 1,只有当计数器为 0 时,其他线程才有机会获取该重入的基本使用方法如下: ```java import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockTest { private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { new Thread(() -> { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " get lock"); Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println(Thread.currentThread().getName() + " release lock"); } }, "Thread-1").start(); new Thread(() -> { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " get lock"); } finally { lock.unlock(); System.out.println(Thread.currentThread().getName() + " release lock"); } }, "Thread-2").start(); } } ``` 在上面的示例代码中,我们创建了两个线程,分别尝试获取重入。由于重入支持可重入性,因此第二个线程可以成功地获取到该,而不会被阻塞。当第一个线程释放后,第二个线程才会获取到并执行相应的操作。 需要注意的是,使用重入时一定要记得在 finally 块中释放,否则可能会导致死的问题。同时,在获取时也可以设置超时时间,避免由于获取失败而导致的线程阻塞问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值