ReentrantLock从名字看 – 重入锁(和synchronized关键字一样),同个线程在重复获取锁的话,会自动获取锁,而不是等待。ReentrantLock中又包含了公平锁、非公平锁的概念。下面列出构造函数和常用方法。
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
// 无参构造函数默认使用非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
// 可以指定非公平锁或者是公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
// Condition用来替代传统的Object的wait()、notify()实现线程间的协作
// 使用方法就和synchronized配合Object一样,condition要在获取到锁的线程中
public Condition newCondition() {
return sync.newCondition();
}
}
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
// 调用AbstractQueuedSynchronizer的acquire尝试将state设置为1
public final void acquire(int arg) {
// 如果获取锁失败,且将此线程丢到队列尾部,阻塞直到获取锁
// addWaiter将线程丢到队列尾部,并返回新node
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 尝试获取锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果没有持有者,队列里面没有人等待
// 则尝试修改 state 获取锁
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果有持有者,且为当前线程,计数器加1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 其它情况,获取锁失败
return false;
}
// 下面两个也是AbstractQueuedSynchronizer的方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 不断尝试去获取锁
for (;;) {
// 获取上个节点,如果上个节点是头节点,尝试给当前节点获取锁
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
// 如果获取到锁,将当前节点设置为头节点
// 并将node中thread的引用设置为null
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 如果当前状态是SIGNAL,则需要获取失败后park线程
// parkAndCheckInterrupt方法调用LockSupport.park(this);
// 阻塞当前线程,结束的时候调用interrupted
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// node.predecessor()中判断当前节点的前面节点,如果为空则抛出空指针
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
/**
* waitStatus表示节点的等待状态
* 1 CANCELLED: 等待超时或者线程中断,结点会被设置为取消状态,不能改变状态。此状态不会去竞争锁
* -1 SIGNAL: 节点的继任者被阻塞了,当此节点release或cancel的时候需要通知
* -2 CONDITION: 节点在条件队列中,不会被用作同步队列节点,直到转移,状态会设置为0
* -3 PROPAGATE: 使用在共享模式头结点有可能牌处于这种状态,表示锁的下一次获取可以无条件传播
* 0: 除以上状态的,会处于这种状态,如新节点
*/
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
// pred为Signal,表示当前节点被阻塞了,需要等待通知,所以要进入park
return true;
if (ws > 0) {
// 如果当前节点前个节点是取消状态,不断往前获取到一个不是这个状态
// 并设置其为当前节点的前节点,用于清理掉中间的节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 将前个节点状态改为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
}
总结:重复获取到锁,计数器加1。获取锁失败则放入节点尾部,并进入等待,直到成为头部节点获取锁。
static final class NonfairSync extends Sync {
final void lock() {
// 直接尝试加锁(这里不管是不是队列在队列头部,直接进行竞争),如果成功则将当前线程设置为持有者
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果不成功,则还是非公平的去尝试获取锁
acquire(1);
}
// 尝试非公平的获取锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
abstract static class Sync extends AbstractQueuedSynchronizer {
final boolean nonfairTryAcquire(int acquires) {
// ......
// 如果当前锁没有持有者
if (c == 0) {
// 与FairSync的tryAcquire只这一个地方不一致
// 不用判断队列是否有等待的,直接尝试获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 当前线程有持有者
else if (current == getExclusiveOwnerThread()) {
intnextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// ......
}
}
总结:非公平锁会直接尝试获取锁而不考虑是否队列中有线程(与其竞争)。如果获取失败了,会放入等待队列(队列中的还是会保证顺序获取锁)。
public void unlock() {
sync.release(1);
}
//
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// LockSupport.unpark(s.thread); 去唤醒一个线程
unparkSuccessor(h);
return true;
}
return false;
}
来看看释放的流程,ReentrantLock.Sync.tryRelease(int releases)方法
protected final boolean tryRelease(int releases) {
// 这里releases是unlock传过来的1,所以c应该是0
int c = getState() - releases;
// 如果不是获取锁的线程调用就抛IllegalMonitorStateException异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 下面将持有线程清空,并重置下同步状态
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
由上面分析nonfairTryAcquire可知,tryLock会尝试获取锁,获取成功返回true,如果是同个线程重复获取会将state增加,如果获取失败,并不会将线程放入队列,所以tryLock不是个阻塞方法。
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
由字面意思很好理解这个方法的作用,在指定时间内尝试获取锁,如果没有获取到则返回false。
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 公平或非公平的获取锁
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
// 这个方法和acquireQueued在for循环中代码有所不同
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
// ......
// 新建一个Node,并将其上一个指向队列末尾
final Node node = addWaiter(Node.EXCLUSIVE);
for (;;) {
// ......
// 如果时间超过了,返回失败
if (nanosTimeout <= 0)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
if (Thread.interrupted())
throw new InterruptedException();
}
// ......
}
}