在java.util.concurrent.locks包中有很多Lock的实现类,ReentrantLock,CountDownLatch,FutureTask,ReentrantReadWriteLock,Semaphore
每个实现类都有不同的使用场景,,,这篇文章介绍FutureTask的源码实现
这里会涉及到 独占锁和共享锁的概念。
独占锁:同一时间只有一个线程获取锁。再有线程尝试加锁,将失败。 典型例子 reentrantLock
共享锁:同一时间可以有多个线程获取锁。 典型例子,本例中的FutureTask
AbstractQueuedSynchronizer的实现非常精妙,看一次两次也许还不能真正领悟到它的精髓,,,,所以,,有时间不停的琢磨阅读,不停的深入,,才能了解到作者的精髓。。。
写这篇文章之前看了很多帖子,有些写的还不错,比如
http://liuinsect.iteye.com/blog/1994831
http://blog.youkuaiyun.com/chen77716/article/details/6641477
但是,看了这些文章还是不能真正理解AQS如何工作,,,比如AbstractQueuedSynchronizer.Node的状态的变化,如何协调线程资源的竞争等等。
光看代码有点难以理解,,,得借助debug工具,才能帮助更容易理解。要让AbstractQueuedSynchronizer可调试,也是件比较麻烦的事情,我花了将近1天的时间才搞定,看这篇帖子 http://blog.youkuaiyun.com/u013603157/article/details/18960217
我们模拟 一个场景,一个线程执行FutureTask的run方法,多个线程调用FutureTask的get方法,假设run方法执行比较慢,,,所有的get方法都要阻塞,,,等run方法执行完后再一个个唤醒所有阻塞
futureTask的get方法调用
V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
if (!tryAcquireSharedNanos(0, nanosTimeout))
throw new TimeoutException();
if (getState() == CANCELLED)
throw new CancellationException();
if (exception != null)
throw new ExecutionException(exception);
return result;
}
调用
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
long lastTime = System.nanoTime();
final Node node = addWaiter(Node.SHARED);//将当前的线程组装成Node,插入
//到队列的尾部,并且返回新生成的node
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {//当队列里面只有一个node的时候(队列头初始node
//不算)满足 p == head
int r = tryAcquireShared(arg);
if (r >= 0) {//认为已经完成(任务状态为(RAN | CANCELLED)
//&& runner == null)
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
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();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
在39行 LockSupport.parkNanos;(this, nanosTimeout);的本地方法,将线程阻塞,,
//这个时候该线程创建的Node对象的state为什么状态呢?
答案在shouldParkAfterFailedAcquire 方法
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 {//新生成的Node的waitStatus状态默认为0,,通过以下的原子操作,状态修改为SIGNAL,然后方法结束后,又会执行一次循环,
//走到第54行位置if (ws == Node.SIGNAL),返回true后,将线程阻塞。为什么要这么做呢??目前还没打看明白,待续。。。
/*
* 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;
}
回到我们模拟的场景,多个调用get方法的线程都在37行的位置阻塞住了,,,,现在FutureTask的run方法运行完了,,,
void innerRun() {
if (!compareAndSetState(READY, RUNNING))
return;
runner = Thread.currentThread();
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
result = callable.call();
} catch (Throwable ex) {
setException(ex);
return;
}
set(result);
} else {
releaseShared(0); // cancel
}
}
假如没有抛出任何异常(为了模拟主干流程,各种分支异常先忽略掉),调用set(result)方法,修改任务状态,并释放阻塞的线程。
protected void set(V v) {
sync.innerSet(v);
}
void innerSet(V v) {
for (;;) {
int s = getState();
if (s == RAN)
return;
if (s == CANCELLED) {
// aggressively release to set runner to null,
// in case we are racing with a cancel request
// that will try to interrupt runner
releaseShared(0);
return;
}
if (compareAndSetState(s, RAN)) {
result = v;
releaseShared(0);
done();
return;
}
}
}
关键是releaseShared(0); 方法释放了所有等待的线程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared 一定会返回true,主要看doReleaseShared();
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
回到39行,线程调用get的时候,将本线程的Note的waitStatus状态设置为SIGNAL,所以156行的
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
将head所指向的Node的waitStatus设置为0,并唤醒head所指向Node的线程,,,然后
doAcquireSharedNanos的LockSupport.parkNanos(this, nanosTimeout);醒了,,代码继续往下走,,,,
在下一轮的循环中,通过setHeadAndPropagate 释放内存,并且唤醒所有阻塞的线程
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);//修改新的head引用,同时释放内存,避免内存泄漏
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;//s指向下一个Node的引用
if (s == null || s.isShared())
doReleaseShared();//唤醒下一个线程。就像多米诺骨牌效应。
}
}