网站首页 > 博客文章 正文
ReentrantLock 是 Java 并发编程中一种重要的同步机制,它比传统的 synchronized 提供了更高的灵活性和功能。下面将从 ReentrantLock 的基本原理、详细使用方法、内部实现机制、注意事项等方面详细说明。
一、ReentrantLock的基本原理
ReentrantLock 是基于可重入的概念设计的锁。当一个线程已经获取了 ReentrantLock 锁,它可以再次进入该锁的同步代码块而不会陷入死锁。这是因为 ReentrantLock 记录了每个线程获取锁的次数,并允许同一线程多次获取它。线程获取锁的次数与释放锁的次数必须匹配,才能真正释放锁。
1.1 AQS(AbstractQueuedSynchronizer)
ReentrantLock 的核心是基于 AQS(抽象队列同步器)实现的。AQS 维护了一个队列,用于管理所有请求锁的线程。每个线程尝试获取锁时,如果当前锁被其他线程持有,则进入等待队列,阻塞等待锁释放。一旦锁释放,AQS 会从队列中唤醒下一个线程,允许它获取锁。
1.2 可重入性
可重入锁允许线程多次进入同一把锁。比如,一个递归方法可以在方法调用链中多次获取同一个锁而不导致死锁。这种特性主要依赖于线程独有的锁计数器。
1.3 公平锁与非公平锁
- 公平锁:按照线程请求锁的顺序进行排队,先到先得。公平锁避免了线程“饥饿”,但性能可能比非公平锁稍低。
- 非公平锁:线程直接尝试获取锁,不关心等待队列中的其他线程,可能会导致某些线程长期等待。非公平锁性能较高,因为它减少了上下文切换和线程调度的开销。
二、ReentrantLock的详细使用方法
ReentrantLock 提供了比 synchronized 更多的控制能力,如尝试获取锁、可中断锁等待、锁的公平性等。下面逐一介绍其主要用法。
2.1 基本使用
在使用 ReentrantLock 时,通常遵循以下步骤:
- 创建锁对象:通过 new ReentrantLock() 创建锁实例。
- 获取锁:使用 lock() 或其他加锁方法获取锁。
- 执行临界区代码:在持有锁的情况下,执行需要保护的共享资源代码。
- 释放锁:使用 unlock() 释放锁,确保其他线程能够继续执行。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock(); // 创建锁
private int counter = 0; // 共享资源
public void increment() {
lock.lock(); // 获取锁
try {
counter++;
System.out.println(Thread.currentThread().getName() + " - Counter: " + counter);
} finally {
lock.unlock(); // 确保在 finally 中释放锁
}
}
public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo();
Thread t1 = new Thread(demo::increment);
Thread t2 = new Thread(demo::increment);
t1.start();
t2.start();
}
}
2.2 尝试获取锁
使用 tryLock() 可以尝试在不阻塞的情况下获取锁。如果当前锁未被持有,则立即获取并返回 true,否则返回 false。这种方法适合用于避免死锁。
if (lock.tryLock()) {
try {
// 执行临界区代码
} finally {
lock.unlock();
}
} else {
// 锁不可用时的处理逻辑
}
还可以设置超时时间来获取锁,避免线程长时间等待锁:
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 获取锁后执行的逻辑
} finally {
lock.unlock();
}
} else {
// 超时未能获取锁
}
} catch (InterruptedException e) {
e.printStackTrace();
}
2.3 可中断锁
lockInterruptibly() 允许线程在等待锁时响应中断。这对于实现中断操作的灵活性非常有用。
try {
lock.lockInterruptibly(); // 可中断的锁获取
try {
// 临界区代码
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
// 处理中断
e.printStackTrace();
}
2.4 公平锁与非公平锁
通过构造函数选择锁的公平性:
- 非公平锁(默认):
- ReentrantLock lock = new ReentrantLock();
- 公平锁:
- ReentrantLock lock = new ReentrantLock(true);
公平锁保证线程按顺序获取锁,但性能较低;非公平锁可能导致“饥饿”,但性能更高。
三、ReentrantLock的内部实现
ReentrantLock 基于 AQS 实现,它通过原子操作 CAS 来控制同步状态。AQS 维护了两个核心状态:
- state:表示锁的状态,0 表示未锁定,1 表示锁定。
- 线程队列:AQS 使用一个 FIFO 队列来管理等待锁的线程。
当线程尝试获取锁时:
- 如果 state == 0,线程成功获取锁,并将 state 设置为 1。
- 如果锁已经被持有(state != 0),线程进入等待队列,直到锁释放后被唤醒。
当持有锁的线程调用 unlock() 时,state 被递减。只有当 state 递减到 0 时,锁才真正释放。
四、使用 ReentrantLock的注意事项
4.1 必须手动释放锁
ReentrantLock 和 synchronized 的主要区别之一是锁的释放必须显式调用 unlock()。忘记释放锁会导致其他线程永远无法获取锁。因此,通常在 try 块中获取锁,在 finally 块中释放锁,以确保异常情况下锁也能被正确释放。
4.2 避免死锁
当多个线程持有多个锁时,容易产生死锁问题。为了避免死锁,可以采用锁的超时机制,或者设计更合理的加锁顺序。
4.3 性能与公平性
非公平锁的性能比公平锁更好,但可能导致某些线程长期无法获得锁。公平锁避免了这种情况,但每次加锁和解锁的性能稍差。在高并发场景下,通常选择非公平锁来提升性能。
4.4 避免锁竞争
过度使用锁可能导致线程竞争和性能下降。在设计多线程应用时,尽量减少临界区的长度,避免不必要的锁。
五、总结
ReentrantLock 提供了比 synchronized 更灵活的锁控制方式。它可以:
- 选择公平锁和非公平锁;
- 提供尝试获取锁和可中断的锁;
- 允许锁的重入;
- 支持手动加锁和解锁的细粒度控制。
- 上一篇: 并发的编程(四)(并发编程的三个要素)
- 下一篇: Java基础——Java多线程(Lock接口详解)
猜你喜欢
- 2024-10-16 “全栈2019”Java多线程第三十一章:如何中断锁上正在等待的线程
- 2024-10-16 阿里巴巴Java性能调优实战:深入了解Lock同步锁的优化方法
- 2024-10-16 线程池:治理线程的法宝(线程池的实现原理和实现方法)
- 2024-10-16 程序员必须要知道的ReentrantLock 及 AQS 实现原理
- 2024-10-16 synchronized 和 ReentrantLock 的实现原理是什么?它们有什么区别
- 2024-10-16 Java线程池核心(十五):工作线程Worker
- 2024-10-16 一文搞懂分布式可重入锁(分布式锁的实现方式及优缺点)
- 2024-10-16 从Java线程池的常用4种写法深入分析线程池的实现原理
- 2024-10-16 Java基础——Java多线程(Lock接口详解)
- 2024-10-16 并发编程解惑之线程(线程并发和并行)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)