一、概念和基础
LockSupport位于java.util.concurrent.locks包下,是线程的阻塞原语,用来阻塞线程和唤醒线程。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在线程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。
LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程。主要是通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作的。,而且park()和unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。因为park() 和 unpark()有许可的存在;调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性。
每个线程都有一个许可(permit),permit只有两个值1和0,默认是0。
二、LockSupport API
1. LockSupport函数列表
// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static Object getBlocker(Thread t)
// 为了线程调度,禁用当前线程,除非许可可用。
static void park()
// 为了线程调度,在许可可用之前禁用当前线程。
static void park(Object blocker)
// 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(long nanos)
// 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkNanos(Object blocker, long nanos)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(long deadline)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline)
// 如果给定线程的许可尚不可用,则使其可用。
static void unpark(Thread thread)
2. park()和park(Object blocker)方法区别
JDK1.6引入对应的拥有Blocker版本。
实际上LockSupport阻塞和唤醒线程的功能是依赖于sun.misc.Unsafe,这是一个很底层的类。比如park()方法的功能实现则是靠unsafe.park()方法。另外在阻塞线程这一系列方法中还有一个很有意思的现象就是,每个方法都会新增一个带有Object的阻塞对象的重载方法。那么增加了一个Object对象的入参会有什么不同的地方了?示例代码很简单就不说了,直接看dump线程的信息。
调用park()方法dump线程:
案例代码:
public class LockSupportWaitForObjectDemo {
public static void main(String[] args)throws Exception {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
//阻塞挂起
LockSupport.park();
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
}
}
运行,cmd下jps 查看所有进程,查到进程PID,然后根据进程PID导出线程堆栈:
// 查看所有进程 可知进程id为16888
C:\Users\nowuseeme>jps
9420 AppMain
15884 Jps
//导出线程堆栈
C:\Users\nowuseeme>jstack 9420>>myjstack.txt
查看myjstack.txt,可得:
"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x0000000002388000 nid=0x4594 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000058b86000 nid=0x4758 waiting on condition [0x0000000059a7e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
at com.test.example04.LockSupportWaitForObjectDemo$1.run(LockSupportWaitForObjectDemo.java:25)
at java.lang.Thread.run(Thread.java:745)
调用park(Object blocker)方法dump线程 :
改下上面的案例代码,调整后为:
public class LockSupportWaitForObjectDemo {
//定义一个内部类作为park(Object blocker)的参数blocker
static class InnerClass{
}
public static final InnerClass innerClass = new InnerClass();
public static void main(String[] args)throws Exception {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
//阻塞挂起
LockSupport.park(innerClass);
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
}
}
运行,cmd下jps 查看所有进程,查到进程PID,然后根据进程PID导出线程堆栈:
// 查看所有进程 可知进程id为16888
C:\Users\nowuseeme>jps
16888 AppMain
15884 Jps
//导出线程堆栈
C:\Users\nowuseeme>jstack 16888>>myjstack1.txt
堆栈如下:
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.5-b02 mixed mode):
"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x0000000002388000 nid=0x3c10 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000058a7a000 nid=0x40b4 waiting on condition [0x0000000059b1e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d5c425e8> (a com.test.example04.LockSupportWaitForObjectDemo$InnerClass)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at com.test.example04.LockSupportWaitForObjectDemo$1.run(LockSupportWaitForObjectDemo.java:25)
at java.lang.Thread.run(Thread.java:745)
通过分别调用这两个方法然后dump线程信息可以看出,带Object的park方法相较于无参的park方法会增加parking to wait for <0x00000000d5c425e8> (a com.test.example04.LockSupportWaitForObjectDemo$InnerClass) 的信息,这种信息就类似于记录“案发现场”,有助于工程人员能够迅速发现问题解决问题。
有个有意思的事情是,我们都知道如果使用synchronzed阻塞了线程dump线程时都会有阻塞对象的描述,在java 5推出LockSupport时遗漏了这一点,在java 6时进行了补充。还有一点需要需要的是:synchronzed致使线程阻塞,线程会进入到BLOCKED状态,而调用LockSupprt方法阻塞线程会致使线程进入到WAITING状态。
三、LockSupport的park()/unpark() 和 Object的wait()/notify()比较
JDK1.8后,ReentrantLock及ReentrantReadWriteLock是基于AQS实现的,AQS内部使用了unsafe类进行操作;LockSupport也是基于unsafe类操作。可以说LockSupport也是阻塞的,但是不会发生Thread.suspend 和 Thread.resume所可能引发的死锁问题。而AQS是非阻塞机制。LockSupport.park()和unpark(),与object.wait()和notify()的区别? 主要的区别应该说是它们面向的对象不同。阻塞和唤醒是对于线程来说的,LockSupport的park/unpark更符合这个语义,以“线程”作为方法的参数, 语义更清晰,使用起来也更方便。而wait/notify的实现使得“阻塞/唤醒对线程本身来说是被动的,要准确的控制哪个线程、什么时候阻塞/唤醒很困难, 要不随机唤醒一个线程(notify)要不唤醒所有的(notifyAll)。
park函数是将当前调用Thread阻塞,而unpark函数则是将指定线程Thread唤醒。
与Object类的wait/notify机制相比,park/unpark有两个优点:
1. LockSupport更加简单
代码为例:
线程A执行一段业务逻辑后调用wait阻塞住自己。主线程调用notify方法唤醒线程A,线程A然后打印自己执行的结果。
public class ObjectWaitDemo {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
try {
obj.wait();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
obj.notify();
}
}
执行这段代码,会发生以下错误:
ava.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.test.example04.ObjectWaitDemo$1.run(ObjectWaitDemo.java:21)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.test.example04.ObjectWaitDemo.main(ObjectWaitDemo.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
原因很简单,wait和notify/notifyAll方法只能在同步代码块里用,将代码修改为如下就可正常运行了:
public class ObjectWaitDemo {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
try {
//wait前添加同步代码块
synchronized (obj) {
obj.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
//notify前添加同步代码块
synchronized (obj) {
obj.notify();
}
}
}
如果换成LockSupport,则操作会简单很多,还是上面的案例,改造如下:
public class LockSupportWaitDemo {
public static void main(String[] args)throws Exception {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
LockSupport.park();
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
Thread.sleep(1000);
LockSupport.unpark(A);
}
}
直接调用即可,不需要在同步块中调用。
2. LockSupport更加灵活
LockSupport的灵活性体现在两个方面:
(1) 以thread为操作对象更符合阻塞线程的直观定义操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程),增加了灵活性。
(2) unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。 相对而言Object的notify必须在wait方法调用之后,才能调用,不然会抛异常。
还是以上面的
public class ObjectWaitNormalDemo {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
try {
synchronized (obj) {
obj.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(sum);
}
});
A.start();
//睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
//Thread.sleep(1000);
synchronized (obj) {
obj.notify();
}
}
}
3. demo
(1) 先interrupt再park
public class LockSupportDemo{
public static void main(String[] args) throws InterruptedException {
String blocker = new String("MyLockSupportDemo");
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("睡觉");
LockSupport.park(blocker);
System.out.println("起床");
System.out.println("是否中断:" + Thread.currentThread().isInterrupted());
}
});
t.setName("DailyLife");
t.start();
t.interrupt();
System.out.println("做噩梦");
}
}
中断后执行park会直接执行下面的方法,并不会抛出InterruptedException,输出结果如下:
做噩梦
睡觉
起床
是否中断:true
(2) 先unpark一次再执行park
public class LockSupportDemo1 {
public static void main(String[] args) throws InterruptedException {
String blocker = new String("MyLockSupportDemo");
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡觉");
LockSupport.park(blocker);
System.out.println("起床");
}
});
t.setName("DailyLife");
t.start();
LockSupport.unpark(t);
System.out.println("提前上好闹钟");
}
}
按照上面说过的,先设置好许可(unpark)再获取许可的时候不会进行等待,正如我们说的那样输出如下:
提前上好闹钟
睡觉
起床
四 源码与深入解析
1. 源码解析
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
/**
* 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象。
* 如果该调用不受阻塞,则返回 null。
* 返回的值只是一个瞬间快照,即由于未解除阻塞或者在不同的 blocker 对象上受阻而具有的线程。
*/
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
}
/**
* 如果给定线程的许可尚不可用,则使其可用。
* 如果线程在 park 上受阻塞,则它将解除其阻塞状态。
* 否则,保证下一次调用 park 不会受阻塞。
* 如果给定线程尚未启动,则无法保证此操作有任何效果。
* @param thread: 要执行 unpark 操作的线程;该参数为 null 表示此操作没有任何效果。
*/
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
/**
* 为了线程调度,在许可可用之前阻塞当前线程。
* 如果许可可用,则使用该许可,并且该调用立即返回;
* 否则,为线程调度禁用当前线程,并在发生以下三种情况之一以前,使其处于休眠状态:
* 1. 其他某个线程将当前线程作为目标调用 unpark
* 2. 其他某个线程中断当前线程
* 3. 该调用不合逻辑地(即毫无理由地)返回
*/
public static void park() {
UNSAFE.park(false, 0L);
}
/**
* 和park()方法类似,不过增加了等待的相对时间
*/
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
/**
* 和park()方法类似,不过增加了等待的绝对时间
*/
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
/**
* 和park()方法类似,只不过增加了暂停的同步对象
* @param blocker 导致此线程暂停的同步对象
* @since 1.6
*/
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
/**
* parkNanos(long nanos)方法类似,只不过增加了暂停的同步对象
* @param blocker 导致此线程暂停的同步对象
* @since 1.6
*/
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
/**
* parkUntil(long deadline)方法类似,只不过增加了暂停的同步对象
* @param blocker 导致此线程暂停的同步对象
* @since 1.6
*/
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
static final int nextSecondarySeed() {
int r;
Thread t = Thread.currentThread();
if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
r ^= r << 13; // xorshift
r ^= r >>> 17;
r ^= r << 5;
}
else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
UNSAFE.putInt(t, SECONDARY, r);
return r;
}
// Hotspot implementation via intrinsics API
private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception ex) { throw new Error(ex); }
}
}
2. Unsafe的park和unpark
LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语。LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,只有两个函数:
/**
* 为指定线程提供“许可(permit)”
*/
public native void unpark(Thread jthread);
/**
* 阻塞指定时间等待“许可”。
* @param isAbsolute: 时间是绝对的,还是相对的
* @param time:等待许可的时间
*/
public native void park(boolean isAbsolute, long time);
上面的这个“许可”是不能叠加的,“许可”是一次性的。
比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。
注意,unpark函数可以先于park调用。比如线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时,它发现已经有“许可”了,那么它会马上再继续运行。
深入HotSpot的源码来看看。每个java线程都有一个Parker实例,Parker类是这样定义的:
class Parker : public os::PlatformParker {
private:
volatile int _counter ;
...
public:
void park(bool isAbsolute, jlong time);
void unpark();
...
}
class PlatformParker : public CHeapObj<mtInternal> {
protected:
pthread_mutex_t _mutex [1] ;
pthread_cond_t _cond [1] ;
...
}
可以看到Parker类实际上用Posix的mutex,condition来实现的。在Parker类里的_counter字段,就是用来记录所谓的“许可”的。
当调用park时,先尝试直接能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为0,并返回:
void Parker::park(bool isAbsolute, jlong time) {
// Ideally we'd do something useful while spinning, such
// as calling unpackTime().
// Optional fast-path check:
// Return immediately if a permit is available.
// We depend on Atomic::xchg() having full barrier semantics
// since we are doing a lock-free update to _counter.
if (Atomic::xchg(0, &_counter) > 0) return;
如果不成功,则构造一个ThreadBlockInVM,然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock mutex并返回:
ThreadBlockInVM tbivm(jt);
if (_counter > 0) { // no wait needed
_counter = 0;
status = pthread_mutex_unlock(_mutex);
否则,再判断等待的时间,然后再调用pthread_cond_wait函数等待,如果等待返回,则把_counter设置为0,unlock mutex并返回:
if (time == 0) {
status = pthread_cond_wait (_cond, _mutex) ;
}
_counter = 0 ;
status = pthread_mutex_unlock(_mutex) ;
assert_status(status == 0, status, "invariant") ;
OrderAccess::fence();
当unpark时,则简单多了,直接设置_counter为1,再unlock mutext返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程:
void Parker::unpark() {
int s, status ;
status = pthread_mutex_lock(_mutex);
assert (status == 0, "invariant") ;
s = _counter;
_counter = 1;
if (s < 1) {
if (WorkAroundNPTLTimedWaitHang) {
status = pthread_cond_signal (_cond) ;
assert (status == 0, "invariant") ;
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
} else {
status = pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
status = pthread_cond_signal (_cond) ;
assert (status == 0, "invariant") ;
}
} else {
pthread_mutex_unlock(_mutex);
assert (status == 0, "invariant") ;
}
}
简而言之,是用mutex和condition保护了一个_counter的变量,当park时,这个变量置为了0,当unpark时,这个变量置为1。
值得注意的是在park函数里,调用pthread_cond_wait时,并没有用while来判断,所以posix condition里的"Spurious wakeup"一样会传递到上层Java的代码里。
五、应用
LockSupport在Java的工具类应用很广泛,以Java里最常用的类ThreadPoolExecutor为例。先看以下代码:
public class PoolExecutorDemo {
public static void main(String[] args)throws Exception {
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(1000);
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,5,1000, TimeUnit.SECONDS,queue);
Future<String> future = poolExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(5000);
return "hello";
}
});
String result = future.get();
System.out.println(result);
}
}
代码中我们向线程池中扔了一个任务,然后调用Future的get方法,同步阻塞等待线程池的执行结果。
Future#get()是如何阻塞住当前线程,线程池执行完任务后又如何唤醒线程的呢?
从 poolExecutor.submit方法查看源码,进入 AbstractExecutorService#submit 方法,查看线程池submit方法的实现:
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
在submit方法里,线程池将我们提交的基于Callable实现的任务,封装为基于RunnableFuture实现的任务,然后将任务提交到线程池执行execute(ftask)。
进入newTaskFor方法,看到:return new FutureTask(callable); 就是简单将任务封装成FutureTask。 future.get() 代码中的future本质上就是FutureTask。
接下来看看 FutureTask#get 方法的实现:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
比较简单,就是判断下当前任务是否执行完毕,如果执行完毕直接返回任务结果,否则进入 awaitDone 方法阻塞等待。
看下 FutureTask#awaitDone 方法实现:
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
awaitDone方法里,首先会用cas操作,将线程封装为WaitNode,保持下来,以供后续唤醒线程时用。再就是调用了LockSupport的park/parkNanos组塞住当前线程。
以上就是线程池阻塞等待任务结果的逻辑,接下来看看线程池执行完任务,唤醒等待线程的逻辑实现。
前边说了,咱们提交的基于Callable实现的任务,已经被封装为FutureTask任务提交给了线程池执行,任务的执行就是 FutureTask#run 方法执行。如下是 FutureTask#run 方法:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
从 result = c.call(); 看起 ,c.call()就是执行我们提交的任务,boolean ran是判断任务是否执行完。执行完后调用了 set(result) 方法,进入set方法:
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
发现set方法调用了finishCompletion方法,想必唤醒线程的工作就在这里边了,看看代码实现:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
在这里边,先是通过cas操作将所有等待的线程拿出来,然后便使用 LockSupport#unpark 唤醒每个线程。
六、参考
java并发编程之LockSupport : https://my.oschina.net/LucasZhu/blog/1536002
https://zhuanlan.zhihu.com/p/35159033
https://www.jianshu.com/p/ceb8870ef2c5
【细谈Java并发】谈谈LockSupport:
https://www.jianshu.com/p/1f16b838ccd8
自己动手写把”锁”—LockSupport深入浅出: https://www.cnblogs.com/qingquanzi/p/8228422.html
Java的LockSupport.park()实现分析:
http://hengyunabc.github.io/java-park/