1、什么是ReentrantLock
ReentrantLock是Java中的一个可重入锁,它提供了与synchronized关键字类似的功能,但具有更多的灵活性和控制力。
2、实现原理
ReentrantLock内部使用了一个同步器类AbstractQueuedSynchronizer(AQS),它是实现锁的基础。AQS使用一个FIFO队列(即等待队列)来管理等待锁的线程。当一个线程请求锁时,如果锁已被其他线程占用,该线程将被放入等待队列中,直到锁可用时再进行竞争。
3、优缺点
3.1、ReentrantLock的优点
3.1.1、ReentrantLock提供了比synchronized关键字更高级的控制和灵活性,可以实现更复杂的锁定机制。
3.1.2、 它支持可重入锁定的概念,即线程可以多次获取同一个锁,而不会导致死锁。
3.1.3、 ReentrantLock提供了公平性选项,允许线程按照请求锁的顺序获取锁。这有助于避免线程饥饿现象。
3.1.4、它支持条件变量,允许线程在特定条件满足之前等待或继续执行。
3.1.5、 ReentrantLock是java.util.concurrent包的一部分,该包提供了其他有用的并发工具。
3.2、ReentrantLock的缺点
3.2.1、 与synchronized关键字相比,ReentrantLock需要更多显式的编码。你需要手动使用lock()和unlock()方法来获取和释放锁。
3.2.2、有可能忘记释放锁,导致潜在的死锁或资源泄漏。需要正确处理异常并使用finally块来确保锁始终被释放。
3.2.3、 在某些情况下,ReentrantLock可能会稍微增加一些开销,尽管差异通常是可以忽略的。
总体而言,ReentrantLock是Java中管理线程同步的强大工具,提供了更多的控制和灵活性。然而,为了避免潜在的问题,需要谨慎和负责任地使用它。
3.3、公平锁&非公平锁
ReentrantLock 还提供了公平锁和非公平锁,通过构造方法接受一个可选的 fair 参数(默认非公平锁):当设置为 true 时表示公平锁;false为非公平锁。
公平锁会按照线程请求锁的顺序来分配锁,确保所有线程都有公平竞争的机会。这有助于避免线程饥饿现象。但是公平锁的效率往往没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量。
3.4、代码示例
创建一个名为 lock 的ReentrantLock对象,并在构造函数中传入 true 来创建公平锁。在每个线程的任务中,我们使用 lock.lock() 方法获取锁,并在任务完成后使用 lock.unlock() 方法释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockFairExample {
// 创建公平锁
private static ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
task();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
task();
}
});
// 启动线程
thread1.start();
thread2.start();
}
public static void task() {
lock.lock();// 获取锁
try {
// 执行需要同步的任务
Thread.sleep(1000); // 模拟处理业务逻辑
System.out.println("Task..."+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();// 释放锁
}
}
}