线程安全(七)ReentrantLock 简介、Condition 条件变量、锁的工作原理、synchronized 与 Lock 的区别

一、ReentrantLock 简介

  • ReentrantLock,也被称为 “可重入锁”,是一个同步工具类,在 java.util.concurrent.locks 包下。这种锁的一个重要特点是,它允许一个线程多次获取同一个锁而不会产生死锁。这与 synchronized 关键字提供的锁定机制非常相似,但 ReentrantLock 提供了更高的扩展性

1.1 Reentrant 的特性:

  1. 可中断: lockInterruptibly()方法获取的锁可以被中断。这提供了另一个相对于 synchronized 关键字的优势,因为 synchronized 不支持响应中断。
  2. 可以设置超时时间: tryLock(long timeout, TimeUnit unit) 方法提供了获取锁超时的功能。它的语义是 在指定的时间内如果获取到锁就返回 true,获取不到则返回 false。这种机制避免了线程无期限地等待锁释放。
  3. 可以设置公平锁: ReentrantLock 在初始化时提供了一个公平锁选项。公平锁会按照线程请求锁的顺序来分配锁,而不是像非公平锁那样允许线程抢占已经等待的线程的锁。公平锁可以减少 “饥饿” 的情况,但也可能降低一些性能。
  4. 支持多个条件变量: ReentrantLock 类中还包含一个 Condition 接口的实现,该接口允许线程在某些条件下等待(await()方法)或唤醒(signal()signalAll()方法)。这提供了一种比使用 wait() 和 notify() 更灵活和更安全的线程通信方式。
  5. 支持重入: ReentrantLock 的一个主要特点是它的名字所表达的含义——“可重入”。简单来说,如果一个线程已经持有了某个锁,那么它可以再次调用 lock() 方法而不会被阻塞。 这在某些需要递归锁定的场景中非常有用。锁的持有计数(state变量)会在每次成功调用 lock() 方法时递归,并在每次 unlock() 方法被调用时递减。

1.2 基本语法

ReentrantLock 的基本语法如下:

// 创建锁对象
private ReentrantLock lock = new ReentrantLock();
try {
   
    // 获取锁
    lock.lock();
} finally {
   
    // 释放锁
    lock.unlock();
}

1.3 ReentrantLock 的主要方法:

  1. void lock():执行此方法时,如果锁处于空闲状态,当前线程将获取到锁。相反,如果锁已经被其他线程持有,将禁用当前线程,直到当前线程获取到锁。
  2. boolean tryLock()如果锁可用,则获取锁,并立即返回 true,否则返回 false。该方法和 lock() 的区别在于,tryLock() 只是 “试图” 获取锁,如果锁不可用,不会导致当前线程被禁用,当前线程仍然继续往下执行代码。而 lock() 方法则是一定要获取到锁,如果锁不可用,就一直等待,在未获得锁之前,当前线程并不继续向下执行。
  3. boolean tryLock(long time, TimeUnit unit)如果锁在给定时间内没有被另一个线程保持,则获取该锁。
  4. void unlock():执行此方法时,当前线程将释放持有的锁。锁只能由持有者释放,如果线程并不持有锁,却执行该方法,可能导致异常的发生。
  5. Condition newCondition()条件对象,获取等待通知组件。该组件和当前的锁绑定,当前线程只有获取了锁,才能调用该组件的 await() 方法,而调用后,当前线程将释放锁。
  6. getHoldCount()查询当前线程保持此锁的次数,也就是执行此线程执行 lock 方法的次数。
  7. getQueueLength()返回正等待获取此锁的线程估计数,比如启动 10 个线程,1个线程获得锁,此时返回的是9。
  8. getWaitQueueLength(Condition condition)返回等待与此锁相关的给定条件的线程估计数。比如 10 个线程,用同一个 condition 对象,并且此时这 10 个线程都执行了 condition 对象的 await 方法,那么此时执行此方法返回 10。
  9. hasWaiters(Condition condition)查询是否有线程等待此锁有关的给定条件(condition)。即对于指定 condition 对象,查询有多少线程执行了 condition.await() 方法。
  10. hasQueueThread(Thread thread)查询给定线程是否等待获取此锁
  11. hasQueueThreads()是否有线程等待此锁
  12. isFair()该锁是否为公平锁
  13. isHeldByCurrentThread()当前线程是否保持锁锁定,线程的执行 lock() 方法的前后分别是 false 和 true。
  14. isLock()此锁是否有任意线程占用
  15. lockInterruptibly()如果当前线程未被中断,获取锁

1.4 lock()、tryLock()、lockInterruptibly() 的区别:

  1. lock() 能获得锁就返回 true,不能的话会一直等待获取锁。

  2. tryLock() 能获得所就返回 true,不能立即返回 false,不会一直等待。

    tryLock(long timeout, TimeUnit unit) 可以增加时间限制,如果超过该时间段还没获得锁,返回 false。

  3. lock()lockInterruptibly(),如果两个线程分别执行这两个方法,但如果此时中断这两个线程,lock() 不会抛出异常,而 lockInterruptibly() 会抛出异常。


二、Condition 条件变量

2.1 什么是 Condition 条件变量?

  • Condition 条件变量是 ReentrantLock 的一部分,它提供了比 Object 监视器方法(如 wait()notify())更高级的线程等待/唤醒机制。Condition 允许更精确地控制线程间的同步,特别是当需要基于特定条件进行等待时。

Condition 接口通常与 ReentrantLock 一起使用,以实现更复杂的线程间协作模式,如:生产者-消费者模式、读者-写者模式等。

2.2 Condition 的核心方法:

下面介绍 3 个 Condition 的方法:

  • await():方法会使当前线程进入等待状态,并释放 ReentrantLock 锁,直到其他线程调用了相应的 signal()signalAll() 方法时才会被唤醒。

  • signal():方法用于唤醒一个等待在该 Condition 上的线程。

    (注意:signal() 不会立即唤醒线程,而是将其放入就绪队列,由操作系统调度器决定何时真正唤醒。)

  • signalAll():方法会唤醒所有等待在该 Condition 上的线程。通常在需要所有等待线程都参与接下来的执行流程时使用。

2.3 Condition 使用示例1:等待与唤醒

下面这个示例展示了如何使用ReentLock和Condition实现线程间通信,通过控制锁的获取和释放以及条件变量的等待和唤醒,可以实现更灵活的线程同步和通信:

ReentrantLockExample.java

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
   
    /**
     * 初始化锁
     */
    private Lock lock = new ReentrantLock();
    // Lock lock = new ReentrantLock(true); // 公平锁
    // Lock lock = new ReentrantLock(false); // 非公平锁

    /**
     * 创建 Condition
     */
    private Condition condition = lock.newCondition(); // 创建 Condition

    /**
     * wait 方法等待。
     */
    private void testConditionWait
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不愿放下技术的小赵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值