AbstractQueuedSynchronizer 独占获取锁流程

AbstractQueuedSynchronizer 是一个同步器,不同并发工具类,通过内部类继承AbstractQueuedSynchronizer 方式,维护状态。
同步器通过模板模式,子类重写相应方法完成状态的维护。
 
同步器依赖内部的同步队列(FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及登台状态等信息构造成为一个节点(Node)并将其加入同步队列,同时阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
 
同步器提供的模板方法:
方法名称
描述
void acquire(int arg) 独占式获取同步状态,如果当前线程获取同步状态成功,则该方法返回,否则,将会进入同步队列等待,该方法会调用重写的tryAcquire(int arg)方法
void acquireInterruptibly(in arg)
与acquire(int arg)相同,但是该方法响应中断,当前线程未获取到同步状态而进入同步队列中,如果当前线程被中断,则该方法会抛出InterruptedException并返回
boolean tryAcquireNanos(int arg,long nanos)
在acquireInterruptibly(int arg)基础上增加了超时限制,如果当前线程在超时时间内没有获取到同步状态,那么将会返回false,如果获取到了返回true
void acquireShared(int arg)
共享式的获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式获取的主要区别是同一时刻可以有多个线程获取到同步状态
void acquireSharedInterruptibly(int arg)
与acquireShared(int arg)相同,该方法响应中断
void tryAcquireSharedNanos(int arg,long nanos)
在acquireSharedInterruptibly(int arg)基础上增加了超时限制
boolean release(int arg)
独占式的释放同步状态,该方法会在释放同步状态之后,将同步队列中的第一个节点包含的线程唤醒
boolean releaseShared(int arg)
共享式的释放同步状态
Collection<Thread> getQueuedThreads()
获取等待在同步队列上的线程集合(队列头节点未算在内,因为其thread为null)
可重写的方法:    
方法名称
描述
protected boolean tryAcquire(int arg)
独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态
protected boolean tryRelease(int arg)
独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态
protected boolean tryRcquireShared(int arg)
共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败
protected boolean tryReleaseShared(int arg)
共享式释放同步状态
protected boolean isHeldExclusively(int arg)
当前同步器释放在独占模式下呗线程占用,一般该方法boast是否被当前线程独占
 
acquire方法:
 
 
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
 
 
acquire方法主要完成了同步状态获取,节点构造,加入同步队列以及在同步队列中自旋等待的相关工作。其主要逻辑是:首先调用自定义同步器实现的tryAcquire(int arg)方法,该方法保证现场安全的获取同步状态,如果同步状态获取失败,则构造同步节点(独占式Node.EXCLUSIVE,同一时刻只能有一个线程成功获取同步状态)并通过addWaiter(Node node)方法将该节点加入到同步队列的尾部,最后调用acquireQueued(Node node,int arg)方法,使得该节点以“死循环”的方式获取同步状态。如果获取不到则阻塞节点中的线程,而被阻塞线程的唤醒主要依靠前驱节点的出队或阻塞队列被中断来实现。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
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) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
通过调用compareAndSetHead(Node expect,Node update)方法来确保节点能被线程安全的添加。在enq(final Node node)方法中,同步器通过“死循环”来保证节点的正确添加,在死循环中只有通过CAS将节点设置为尾节点后,当前线程才能从该方法返回,否则当前线程不断的尝试设置。可以看出,enq(final Node node)方法将并发添加节点的请求通过CAS变得“串行化”了。
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;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
节点进入同步队列后,就进入了一个自旋的过程,每个节点(线程)都在自省的观察,当条件满足,获取到了同步状态,就可以从这个自旋过程中退出,否则依旧留在这个自旋过程中(并会阻塞节点的线程)。
在acquireQueued(final Node node,int arg)方法中,当前循环在“死循环”中尝试获取同步状态,而只有前驱节点是头节点才能尝试获取同步状态。当前驱节点不是头节点时,shouldParkAfterFailedAcquire检查判断前驱节点的状态, 第一次调用,将前驱节点状态设为SIGNAL,第二次 返回true,当前线程被阻塞。
以上为独占式同步状态获取流程。
 
 
 
 

猜你喜欢

转载自edgar108.iteye.com/blog/2288670