1.什么是ReentrantLock
1.1ReentrantLock 与Synchronized区别
在面试中询问ReentrantLock与Synchronized区别时,一般回答都是
ReentrantLock
- ReentrantLock是JDK方法,需要手动声明上锁和释放锁,因此语法相对复杂些;如果忘记释放锁容易导致死锁
- ReentrantLock具有更好的细粒度,可以在ReentrantLock里面设置内部Condititon类,可以实现分组唤醒需要唤醒的线程
- RenentrantLock能实现公平锁
Synchronized
- Synchoronized语法上简洁方便
- Synchoronized是JVM方法,由编辑器保证枷锁和释放
与 synchronized 的异同
ReentrantLock 和 synchronized 都是用于线程的同步控制,但它们在功能上来说差别还是很大的。对比下来 ReentrantLock 功能明显要丰富的多。下面简单列举一下两者之间的差异,如下:
特性 | synchronized | ReentrantLock | 相同 |
---|---|---|---|
可重入 | 是 | 是 | ✅ |
响应中断 | 否 | 是 | ❌ |
超时等待 | 否 | 是 | ❌ |
公平锁 | 否 | 是 | ❌ |
非公平锁 | 是 | 是 | ✅ |
是否可尝试加锁 | 否 | 是 | ❌ |
是否是Java内置特性 | 是 | 否 | ❌ |
自动获取/释放锁 | 是 | 否 | ❌ |
对异常的处理 | 自动释放锁 | 需手动释放锁 | ❌ |
除此之外,ReentrantLock 提供了丰富的接口用于获取锁的状态,比如可以通过isLocked()
查询 ReentrantLock 对象是否处于锁定状态, 也可以通过getHoldCount()
获取 ReentrantLock 的加锁次数,也就是重入次数等。而 synchronized 仅支持通过Thread.holdsLock
查询当前线程是否持有锁。另外,synchronized 使用的是对象或类进行加锁,而 ReentrantLock 内部是通过 AQS 中的同步队列进行加锁,这一点和 synchronized 也是不一样的。
1.2ReentrantLock特征介绍
JAVA的java.util.concurrent框架中提供了ReentrantLock类(于JAVA SE 5.0时引入),ReentrantLock实现了lock接口,具体在JDK中的定义如下:
public class ReentrantLock implements Lock, java.io.Serializable {
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
}
看到一个类首先就需要知道它的构造方法有哪些,ReentrantLock有两个构造方法,一个是无参的 ReentrantLock() ;另一个含有布尔参数public ReentrantLock(boolean fair)。后面一个构造函数说明ReentrantLock可以新建公平锁;而Synchronized只能建立非公平锁。
那么Lock接口有哪些方法
Lock接口中有lock和unlock方法,还有newCondition() 方法,这就是上面说的ReentrantLock里面设置内部Condititon类。由于ReentrantLock实现了Lock接口,因此它必须实现该方法,具体如下:
public Condition newCondition() {
return sync.newCondition();
}
返回Condition类的一个实例。
2 ReentrantLock其它方法介绍
在介绍它的其它方法前,要先明白它的使用方法,以下JDK中的建议:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
建议用try,在finally里面一定要释放锁,防止被中断时锁没释放,造成死锁
lock()
public void lock() {
sync.lock();
}
如果该锁没被其它线程获得,则立即返回;并且把 lock hold count的值变位1.
unlock()
public void unlock() {
sync.release(1);
}
如果当前线程是该锁的持有者,则保持计数递减。 如果保持计数现在为零,则锁定被释放。 如果当前线程不是该锁的持有者,则抛出IllegalMonitorStateException 。
isFair()
public final boolean isFair() {
return sync instanceof FairSync;
}
判断该锁是不是公平锁
newCondition()
public Condition newCondition() {
return sync.newCondition();
}
返回新的ConditionObject对象。
Condition接口中的方法
-
await(): void await() throws InterruptedException;
Condition接口中的方法,导致当前线程等到发信号。 -
siginal()
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
唤醒一个等待该条件的线程去获得锁(第一个)。
- signalAll():唤醒所有等待线程。