Java中的重入锁(ReentrantLock)是一种实现了Lock接口的可重入互斥锁,由Java并发包java.util.concurrent.locks
提供。重入锁允许同一个线程在获取锁后,可以再次获取该锁,而不会被自己持有的锁阻塞。这种特性使得在递归调用或者嵌套同步块中使用锁时更加方便和安全。
重入锁的基本特性:
-
可重入性(Reentrancy):
- 当一个线程获得了重入锁后,可以再次调用lock()方法获取锁,不会因已经持有锁而被阻塞。每次lock()操作都会增加锁的持有计数,unlock()操作则会减少计数,当计数为0时,锁才会被真正释放。
-
公平与非公平锁:
- ReentrantLock可以指定为公平锁或非公平锁,默认是非公平锁。
- 公平锁:按照线程获取锁的先后顺序来分配锁,先请求锁的线程在锁释放时将优先获得锁。
- 非公平锁:在锁释放时,任何等待锁的线程都有可能获得锁,不保证锁获取的绝对顺序。
- ReentrantLock可以指定为公平锁或非公平锁,默认是非公平锁。
-
条件变量:
- ReentrantLock除了基本的锁功能外,还提供了Condition对象,可以用来实现线程间的条件通信,类似于synchronized关键字配合wait()和notify()方法的功能,但提供了更高的灵活性。
-
手动管理:
- 使用ReentrantLock时,需要明确调用lock()和unlock()方法来获取和释放锁,这与synchronized关键字自动管理锁的机制不同。因此,使用ReentrantLock需要注意确保在finally块中释放锁,以避免发生异常时锁得不到释放导致死锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final Lock lock = new ReentrantLock();
public void doSomething() {
// 获取锁
lock.lock();
try {
// 临界区代码
// ...
// 再次获取锁(重入)
lock.lock();
try {
// 更深层次的临界区代码
// ...
} finally {
// 释放第二次获取的锁
lock.unlock();
}
} finally {
// 释放第一次获取的锁
lock.unlock();
}
}
}
在现实生活中,重入锁的机制类似于家庭钥匙管理系统的一个抽象概念。想象一个家庭的主卧室有一个带锁的门,父母手中有一把万能钥匙,可以打开家中所有的锁。
重入锁的例子:
-
父亲(线程A)拿着万能钥匙进入主卧室(获取锁),此时父亲对卧室拥有访问权。
-
在主卧室里面,父亲发现衣柜里还有一个小抽屉(子临界区)也上了锁,但他手中的万能钥匙依然可以打开这个抽屉(再次获取锁)。
-
父亲查看完抽屉的内容后,关闭并锁上抽屉(释放第二次获取的锁)。
-
最后,父亲离开主卧室并将主卧室的门锁上(释放第一次获取的锁)。
在Java的重入锁机制中,这个“万能钥匙”就好比是线程的锁计数器,当线程首次获取锁时,计数器加1,再次获取时继续加1。释放锁时,计数器递减,只有当计数器归零时,锁才会真正释放给其他线程。这样设计的好处在于,当一个线程在持有锁的情况下需要再次访问同一把锁保护的其他资源时,不会因为自己已经持有锁而被阻塞,从而避免了死锁的发生。