Reentrant Lock的公平性原理解析
发布时间: 2024-01-24 11:42:29 阅读量: 36 订阅数: 32
# 1. 什么是Reentrant Lock及其应用
Reentrant Lock是一种线程同步的工具,它与synchronized关键字相似,但提供了更多灵活性和可控性。Reentrant Lock允许一个线程多次获得锁,而synchronized在这种情况下会导致死锁。
#### Reentrant Lock的应用场景
Reentrant Lock通常用于以下情况:
- 需要更灵活的锁定机制,例如在等待锁的同时能够响应中断
- 需要尝试获取锁并在获取失败时进行一定的处理
- 需要公平性的锁定机制
- 需要实现锁的多条件等待
- 需要支持可定时的、可轮询的锁获取方式
接下来,我们将深入了解Reentrant Lock的工作原理。
# 2. Reentrant Lock的工作原理
在前面的章节中,我们已经了解到Reentrant Lock是一种可重入锁,它可以让同一个线程多次获取锁而不会导致死锁。那么究竟什么是可重入锁,Reentrant Lock又是如何实现可重入性的呢?
### 2.1 可重入锁的概念
可重入锁是指线程可以多次获取同一个锁而不会产生死锁的情况,比如线程在持有锁的情况下再次请求该锁,它就可以继续获取该锁,而不会被自己所持有的锁所阻塞。
### 2.2 Reentrant Lock的实现原理
Reentrant Lock的可重入性是通过一个名为"线程拥有的锁计数器"来实现的。该计数器会记录当前线程已经获取该锁的次数,当计数器为0时表示该锁是未被任何线程所持有的。当一个线程获取了该锁之后,计数器会加1。
当同一个线程再次请求该锁时,它会发现锁的计数器已经不为0,这时不会阻塞线程而是简单地增加计数器的值。当线程退出了锁所保护的代码块后,计数器会递减1。只有当计数器递减到0时,锁才会被完全释放,其他线程才可以获取该锁。这种方式确保了可重入性。
下面是一个简单的示例代码,演示了Reentrant Lock的可重入性:
```java
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(() -> {
// 第一次获取锁
lock.lock();
try {
System.out.println("线程1获得了锁");
// 第二次获取锁
lock.lock();
try {
System.out.println("线程1再次获得了锁");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
lock.lock();
try {
System.out.println("线程2获得了锁");
} finally {
lock.unlock();
}
}).start();
}
}
```
在上述示例代码中,我们创建了一个Reentrant Lock实例,并创建了两个线程。线程1首先获取锁,并在锁保护的代码块内再次获取锁,然后释放锁。而线程2则只是单纯获取锁,并在保护的代码块内执行。
通过运行上述代码,我们可以看到输出的结果为:
```
线程1获得了锁
线程1再次获得了锁
线程2获得了锁
```
从输出结果可以看出,线程1能够成功地第二次获取锁,而线程2只能获取一次锁。这就是可重入锁的特点。
### 2.3 Reentrant Lock与Synchronized的区别
相比于传统的synchronized关键字,Reentrant Lock提供了更加灵活和高级的功能,但是使用Reentrant Lock也相对来说更加复杂。下面我们来对比一下它们之间的区别。
1. Synchronized关键字是由Java虚拟机提供的一种内置锁,而Reentrant Lock是一个基于JUC的类库实现的。
2. Synchronized关键字的锁是不可中断的,只能等待获取锁,而Reentrant Lock可以通过lockInterruptibly()方法实现可中断的等待锁。
3. Synchronized关键字不支持公平性,而Reentrant Lock可以通过构造函数传入参数决定是否支持公平锁。
4. Synchronized关键字会自动释放锁,而Reentrant Lock则需要显式地调用unlock()方法来释放锁。
根据实际需求,我们可以选择使用Synchronized或Reentrant Lock,但通常来说,Reentrant Lock更适用于一些复杂的并发控制场景。
### 小结
本章中,我们详细讲解了Reentrant Lock的工作原理,它通过线程拥有的锁计数器来实现可重入性。相比于传统的synchronized关键字,Reentrant Lock更加灵活和高级。在下一章节,我们将讨论Reentrant Lock的公平性。
# 3. Reentrant Lock的公平性
在前面的章节中我们已经了解了Reentrant Lock的基本概念和工作原理。在本章中,我们将重点关注Reentrant Lock的公平性。
#### 1. 什么是公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。也就是说,先到先得,先申请锁的线程会先获取到锁。公平锁遵循"先到先得"的原则,保证线程获取锁的公平性。在Reentrant Lock中,通过使用参数为`true`的构造方法可以创建公平锁。
#### 2. Reentrant Lock的公平性原理
Reentrant Lock的公平性是通过一个叫做`AQS(AbstractQueuedSynchronizer)`的同步器来实现的。AQS内部维护了一个双向队列,用于管理等待获取锁的线程。当一个线程请求锁时,如果锁没有被其他线程占用,则线程可以直接获取到锁。如果锁被其他线程持有,则线程会被加入到等待队列中,等待锁被释放。
在公平锁的情况下,当一个线程请求锁时,如果锁已经被其他线程所持有,则会先判断当前线程是否是等待队列中的第一个线程。如果是,则允许当前线程获取锁;如果不是,则当前线程会被加入到等待队列中,等待前面的线程释放锁后再进行竞争。
通过这种方式,公平锁保证了线程获取锁的顺序是按照申请锁的顺序来进行的,从而保证了锁的公平性。
下面是一个使用Reentrant Lock的公平锁的示例代码:
```java
import java.util.concurrent.locks.ReentrantLock;
public class FairLockDemo {
private ReentrantLock lock = new ReentrantLock(true);
public void print() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得了锁");
Thread.sleep(1000);
```
0
0