【JUC-7】ReentrantLock (可重入锁)基础

ReentrantLock (可重入锁)

ReentrantLock实现了Lock接口, 内部通过继承AQS, 实现了一个同步器. 可以通过同步器来创建Condition条件变量, 可以用作容器, 存放不同条件的等待线程.

说明ReentrantLock与AQS的关系 类图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNl3dfGd-1688483369185)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1688306634769.png)]

相对于synchronized, 都支持可重入. 它还具备如下特点:

  1. 可重入性:与synchronized关键字一样,ReentrantLock是可重入的,即同一个线程可以多次获取同一个锁而不会造成死锁。
  2. 公平性选择:ReentrantLock提供了公平锁和非公平锁的选择。公平锁会按照线程的请求顺序来获取锁,而非公平锁则不保证公平性,可能导致某些线程长时间等待。
  3. 锁获取和释放:使用ReentrantLock可以通过lock()方法获取锁,并通过unlock()方法释放锁。需要注意的是,获取锁后一定要在合适的地方释放锁,通常使用finally块来确保锁的释放。
  4. 条件变量:ReentrantLock提供了条件变量(Condition)的支持,通过newCondition()方法创建条件变量。条件变量可以让线程在特定的条件下等待或被唤醒,常与锁配合使用。
  5. 中断响应:ReentrantLock支持对线程的中断响应。在等待锁的过程中,可以通过调用lockInterruptibly()方法使线程对中断作出响应。
  6. 锁的可见性:与synchronized关键字一样,ReentrantLock同样保证了锁的可见性,即当一个线程释放锁时,对应的变量修改将对其他线程可见。

lockInterruptibly() :

是一个可以被打断的获取锁操作, 相当于是支持interrupt()方法的lock()方法. 普通的lock()方法如果被其他线程阻塞, 是无法通过interrupt()方法打断的, lockInterruptibly()方法, 是在加锁的时候如果被阻塞, 允许被打断.

Condition :

Condition是条件变量对象, 相当于是一个存放等待线程的容器, 比如在线程池中, 就会使用到Condition, 来存放不同的工作线程.

阻塞方法: await() 相当于 object对象的wait()

唤醒方法: signal() 相当于 object对象的 notify()

使用方法: 使用condition之前, 必须调用ReentrantLock对象的lock方法, 之后调用condition.await() 方法, 就把该线程加到condition对象中等待了, 后续执行signal() 方法, 就会从condition容器中随机唤醒一个线程, 继续执行

线程池是如何使用Condition的?

线程池没有直接使用Condition, 而是通过缓冲队列来使用Condition, 让线程没有任务执行时, 进入wait状态, 当有任务时, 会被唤醒执行任务.

例如, 在ArrayBlockingQueue队列中, 维护了两个condition, 一个是notEmpty, 当线程池调用队列的take方法获取任务执行的时候, 队列会判断当前队列中是否存在等待中的任务, 如果存在, 则取出执行, 如果没有, 则进入notEmpty Condition等待. 源码如下

线程池从队列获取任务, 队列中没任务, 则线程进入notEmpty等待

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            // 队列中任务数量为0
            while (count == 0)
                // 进入notEmpty等待
                notEmpty.await();
            // 如果不为0 , 取出执行任务
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

线程往队列中加入任务时, 如果队列满了, 则进入notFull中等待

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
  1. notEmpty条件:用于控制任务队列中是否有任务可执行的条件。当任务队列为空时,线程池的工作线程会等待在notEmpty条件上,直到有新的任务被提交到任务队列中,此时会调用notEmpty.signal()方法来通知等待的工作线程继续执行任务。
  2. notFull条件:用于控制任务队列中是否还有空间可以接受新的任务的条件。当任务队列满时,如果线程池的最大线程数未达到限制,新提交的任务会直接创建新的工作线程来执行。当任务队列中有空闲位置时,线程池的工作线程会等待在notFull条件上,直到有新的任务被提交或者有工作线程被空闲释放,此时会调用notFull.signal()方法来通知等待的工作线程继续执行任务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

干饭两斤半

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值