ReentrantLock编码示例
Lock lock = new ReentrantLock();//①
lock.lock();//②
try {
//do something
} finally {
lock.unlock();//③
}
①new ReentrantLock()
public ReentrantLock() {
sync = new NonfairSync();
}
无参数构造函数初始化一个非公平锁。关于公平锁和非公平锁,以及Java锁参考Java锁。
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {...}
Sync类是一个内部类,继承实现的AQS锁框架(参考文档AQS)。
在ReentrantLock中,Sync类又衍生出两个子类分别为NonfairSync类和FairSync类,也就是非公平锁和公平锁的实现。
static final class NonfairSync extends Sync {...}
static final class FairSync extends Sync {...}
②lock.lock()
无参数构造函数初始化一个非公平锁。lock()函数底层调用类NonfairSync的lock函数。
- if语句:利用底层的CAS操作比较并且赋值AQS框架中的变量state值,如果设置成功(state由0到1),当前线程获得锁,并且设置为锁的owner。
- else语句:CAS操作失败,调用父类的acquire()函数获取锁,父类acquire()函数最终调用的就是子类NonfairSync的tryAcquire()函数。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
类Sync中定义的非公平锁获取函数nonfairTryAcquire()。说明在代码注释中。
final boolean nonfairTryAcquire(int acquires) {
//获得当前的线程
final Thread current = Thread.currentThread();
//取得状态变量state的值
int c = getState();
//如果state的值是0,没有锁。
if (c == 0) {
//CAS操作设置state,尝试获得锁。
//这里进行了第二次的比较(第一次在NonfairSync类的lock函数中)
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//当前线程已经获得锁
else if (current == getExclusiveOwnerThread()) {
//增加state
int nextc = c + acquires;
//如果结果小于0,意味着整型溢出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//可以重入,设置state
setState(nextc);
return true;
}
return false;
}
如果获得锁,线程继续执行接下来的逻辑。那么没有获得锁呢?tryAcquire()返回false,未获得锁,执行的是acquireQueued()线程入队等待的逻辑。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
线程未获得锁的情况下要入队等待。等待队列也是公平锁和非公平锁逻辑操作的差别所在,非公平锁是当前所有的线程都可以执行CAS操作尝试获得锁,而公平锁只有队列中第一个线程可以获得锁。
//返回新的节点
private Node addWaiter(Node mode) {
//当前线程被封装为链表的Node节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//插入到尾节点之后
Node pred = tail;
//尾节点不为null,当前队列中已经存在等待的线程
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//初始化等待队列的第一个节点
enq(node);
return node;
}
//入队后的节点
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; // help GC
failed = false;
return interrupted;
}
//线程判断自己是否需要阻塞
//当前线程节点的前驱信号设置为Node.SIGNAL,意味着自己将等待被唤醒。
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
总结一下,等待的线程节点会自旋再次尝试获得锁。如果仍旧没有成功,判断自己是否需要被阻塞,根据需要阻塞自己,等待其他线程唤醒自己后,继续尝试获得锁。
③lock.unlock()
锁的释放过程与锁的获取过程刚好相反,怎么获得的就要怎么释放。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
第一步:尝试释放锁tryRelease()
protected final boolean tryRelease(int releases) {
//减少状态变量state
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果结果为0,没有线程持有锁
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//更新state
setState(c);
return free;
}
第二步:释放成功后,唤醒等待队列中的后继线程unparkSuccessor()
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}
最后,被唤醒的后继节点,执行上面介绍的acquireQueued()函数继续尝试获得锁。
总结
- 初始化
- 无参数构造函数初始化非公平锁
- 可以传递参数设置公平锁
- 加锁
- 没有获得锁的线程会进入等待队列。
- 自旋技术,大多数的锁持有时间短暂,而且大多数线程竞争不严重。基于这样的推断,运用自旋技术等待一会,马上尝试可以减少线程阻塞和唤醒的消耗。
- 如果迟迟得不到锁,线程会被挂起。
- 释放锁
- 释放自己持有的锁
- 唤醒等待队列中自己的后继线程
网友评论