昨天ConcurrentHashMap中内部类分段锁segment直接继承ReentrantLock类,今天康康ReentrantLock。
ReentrantLock类构造方法及成员:
private final Sync sync;
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
由构造方法可知,根据参数fair是否是boolean来选择FairSync或者NonFairSync的实现(公平锁和非公平锁)。
在这里嗯调用内部类sync的方法:
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
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);
}
下来我们先去看看AbstractQueuedSynchronizer的实现。
AbstractQueuedSynchronizer的成员:
private transient volatile Node head;
// 双向队列头结点
private transient volatile Node tail;
// 双向队列尾结点
private volatile int state;
// state代表该锁的占用情况,如果为01则代表当前锁并没有被任何线程调用。
队列中的具体线程通过内部类Node类来存储,Node类成员:
int类型的量分别表示Node处于的状态。
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
// SHARED和EXCLUSIVE两个成员决定Node处于共享模式还是独占模式。
static final int CANCELLED = 1;
// CANCELLED状态表示该节点已经由于超时或者是打断已经被取消,
// 一旦有节点进入这个阶段就再也不会转变状态,处于这个状态的节点不会再被阻塞
// 该状态是唯一大于0的状态量
static final int SIGNAL = -1;
// SIGNAL状态表示,该节点的继任节点已经处于阻塞状态,
// 而此时,当现在的节点在被释放或者取消的时候需要唤醒该节点的后继节点。
static final int CONDITION = -2;
// CONDITION状态表示该节点已经处于一个条件队列当中,
// 该节点在转变之前不能在视为同步队列上的节点,
// 如果被转换时,节点状态会变为0(节点的初始状态)
static final int PROPAGATE = -3;
// PROPAGATE状态表示在共享状态下的节点的下一个节点获取锁的时候可以保证锁的取得,
//及时有阻塞的发生
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
// nextWaiter表示该节点在条件队列上的后继节点。
出于线程安全的考虑,AbstractQueuedSynchronizer类内部采用了unsafe类来保证成员在高并发条件下读写的准确性。线程的挂起与恢复均依赖于unsafe类
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
当ReentrantLock类实现公平锁前提下,从外部调用lock()方法的时候会直接调用内部类的lock()方法。
public void lock() {
sync.lock();
}
final void lock() {
acquire(1);
}
但在内部FairSync中,调用lock()方法的时候,会直接调用AbstractQueuedSynchronizer类的acquire()方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
先调用了tryAcquire()方法,但是在AbstractQueuedSynchronizer类中并没有具体实现,真正的具体实现在FairSync类中。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 先判断state是否为0,也就是当前锁是否已经被别的线程占用
// 如果为0,需调用hasQueuedPredecessors()来确认在同步队列中已经没有节点在正在等待。
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
在这之后通过cas给锁的state加上所传进入的值,以确保表示该锁已经被占用,之后设置当前线程为占用锁的进程。
如果在之前state已经不为0,则要在判断一下该线程是否已经是取得所得线程,如果是,给state加上所传入的数字(记得小于Interger.MAX_VALUE),在这之后也算取得锁了。
否则取得锁的行为算是失败。既然如此,我们需要将线程作为节点存入队列中了。
首先通过addWaiter()方法生成Node(独占模式)加入队列。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
上面是关于加入队列的方法,在成功加入队列之后,就会调用acquireQueued()方法来试图在队列中取得锁。
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)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
从这里循环部分可以看出,如果当前节点的前驱就是头结点。同时在之前的tryAquire()方法中成功获得了锁,那么当前节点也顺利获得了锁。如果当前节点并不是头结点的下一个节点或者在之前取得的锁失败,就会调用shouldParkAfterFailedAquire()方法判断这个节点队列上下一个将会执行到的节点。
shouldParkAfterFailedAquire():
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
这里可以看出如果该节点的前驱节点的状态正好是SIGNAL,那么根据状态可知,这里的线程符合需要被挂起的条件。如果不是,则需要继续判断。如果前驱节点已经被处于取消状态,那么就需要不断往该节点的前驱,往之前的节点找到不是处于取消状态的节点。如果不是上面的两种状态得到节点,那么就要将前驱节点的状态改为SIGNAL,在下一次循环中试图将线程挂起。
如果符合线程挂起条件,则通过LockSupport类调用unsafe类方法将线程挂起。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
unsafe.park(false, 0L);
setBlocker(t, null);
}
unsafe类通过本地方法完成线程阻塞的目的。
如果在上面的调用acquireQueued()方法出现了异常,则会在canselAquire()方法中取消尝试获得锁的操作。
private void cancelAcquire(Node node) {
if (node == null)
return;
node.thread = null;
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
Node predNext = pred.next;
node.waitStatus = Node.CANCELLED;
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
node.next = node;
}
}
在取消的过程中,首先将所要取消的Node类中的线程置为Null,之后不断在队列中依次从前驱节点中找到一个不是取消状态的节点当做当前节点的前驱,并且将取消的节点从队列中清除。之后将该节点的状态置为取消。接下来,如果要取消的节点已经为尾节点,则将该节点的前驱节点置为尾结点。如果在该节点的前驱不是头结点,则将其前驱置为SIGNAL状态,并将要取消的节点的后继赋给前驱的后继。
如果要取消的节点已经是头结点了,或者再给前驱设置SIGNAL状态失败,则需要调用unparkSuccessor()方法来恢复已经处于挂起状态的后继节点。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
在这个方法中,如果所需要唤醒的节点为空,或者已经为取消状态,则需要从尾节点到当前节点选择离当前节点最近的没有被取消的节点。
最后通过LockSupport来恢复被挂起的线程。
加锁的过程完毕。
现在继续看从外部调用unlock()方法解锁:
public void unlock() {
sync.release(1);
}
直接调用内部类sync的release()方法。具体实现在AbstractQueuedSynchronizer类中
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
首先尝试给state减少传递进来的值,tryRelease()方法在Sync中实现。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 确保执行解锁操作的线程是当前占有锁的线程,否则会直接抛出错误。
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
如果此时state已经为0,则说明已经没有线程再持有锁了。那么就可以返回true。
从队列中挂起等待回复的线程,选择一个调用刚才已经解释过的unparkSuccessor()恢复一个线程重新在队列中尝试获得锁。
解锁就这些。
这里看看nonFairSync和FairSync区别,在lock()方法中:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
nonFairSync的加锁过程在一开始就就将state加一,而FairSync先获得锁再取得锁。
条件队列的相关分析,外部通过调用newCondition()方法获得新的Condition对象。
final ConditionObject newCondition() {
return new ConditionObject();
}
而具体的Contition实现类ConditionObject类在AbstractQueuedSynchronizer作为内部类。
// 分别指向条件队列头尾Node
private transient Node firstWaiter;
private transient Node lastWaiter;
在addConditionWaiter()方法中在条件节点的末尾加入节点作为尾节点。
private Node addConditionWaiter() {
Node t = lastWaiter;
.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
在加入之前需要判断尾节点是否是符合条件队列要求的节点,否则调用unlinkCancelledWaiters()方法,将条件队列中不符合要求的节点都清除掉。
unlinkCancelledWaiters():
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
在唤醒条件队列 的过程中直接调用dosignal()方法将头结点唤醒:
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
关于唤醒的过程,将节点的状态变为0,并将该节点放入锁的并发队列,如果放入之后前驱节点已经处于取消状态,或者给前驱节点设置SIGNAL状态,则直接将当前线程从挂起中恢复。
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
关于Condition最为常用的await()方法:
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
当调用await()方法时,先生成节点放入条件队列中。直到外界调用signal()方法将条件队列中的头结点放入并发队列中。否则线程一直处于挂起状态。当处于并发队列中并且从挂起状态恢复之后,则会尝试在队列中取得锁,与并发节点的在并发队列中获取锁的操作一样,都调用acquireQueued()方法。