原理
官方说是线程阻塞的基本原语,可以用来创建锁或者其它的同步类(ReentrantLock基于该类实现)。每个线程使用LockSupport都会关联一个 “许可证”。
某个线程调用LockSupport.park,如果对应的“许可证”可用,则此次调用立即返回,否则线程将会阻塞直到中断发生、超时或者许可证状态变为可用。LockSupport.unpark(thread)可以使线程的许可证可用,但是unpark不能累积,只能使许可证从不可用到可用,不能累加可用次数。也就是说连续调用unpark和调用一次unpark效果是一样的。
public class LockSupport {
/**
* 给某个线程颁发许可证,唤醒线程
*/
public static void unpark(Thread thread);
/**
* 阻塞调用线程直到线程中断或者许可证状态变为可用,如果许可证可用(之前调用过一次unpark)则立
* 即返回
* blocker是设置阻塞线程的对象,追踪问题用吧
*/
public static void park(Object blocker);
/**
* 阻塞调用线程直到线程中断、等待nanos纳秒或许可证状态变为可用,如果许可证可用(之前调用过一
*次unpark)则立即返回
* blocker是设置阻塞线程的对象,追踪问题用吧
*/
public static void parkNanos(Object blocker, long nanos);
/**
* 阻塞调用线程直到线程中断、到来截止时间(毫秒)或者许可证状态变为可用,如果许可证可用(之前调
* 用过一次unpark)则立即返回
* blocker是设置阻塞线程的对象,追踪问题用吧
*/
public static void parkUntil(Object blocker, long deadline);
/**
* 阻塞调用线程直到线程中断或者许可证状态变为可用,如果许可证可用(之前调用过一次unpark)则立
* 即返回
*/
public static void park();
/**
* 阻塞调用线程直到线程中断、等待nanos纳秒或许可证状态变为可用,如果许可证可用(之前调用过一
*次unpark)则立即返回
*/
public static void parkNanos(long nanos);
/**
* 阻塞调用线程直到线程中断、到来截止时间(毫秒)或者许可证状态变为可用,如果许可证可用(之前调
* 用过一次unpark)则立即返回
*/
public static void parkUntil(long deadline);
}
park底层调用的是Unsafe类的park方法
public final class Unsafe {
/**
* isAbsolute表示绝对时间时间还是相对时间
* time 时间,isAbsolute为true就是毫秒时间戳,绝对时间;isAbsolute为false就是纳秒相对时间,多
* 少纳秒以后醒来
**/
public native void park(boolean isAbsolute, long time);
}
LockSupport的效果跟Thread.sleep类似,但是它跟线程sleep有以下不同:
- sleep和park都阻塞当前线程的执行,两者都不释放占有的锁资源
- sleep需要指定时间,park可指定也可不指定;
- sleep不能被其它线程唤醒,park可以被其它线程唤醒;
- sleep需要捕获InterruptedException异常,park不需要;
- park如果想知道唤醒的原因是否是InterruptedException,需要去判断线程的中断状态Thread.interrupted()。
应用举例
jdk的很多同步操作是基于LockSupport做的,例如FutureTask的get操作(阻塞获取Future的结果),当任务状态没有完成时线程调用LockSupport.park,并将作为FutrueTask对象的等待节点放在waiters链表里面。源码如下所示(关键部分笔者添加了注释):
public class FutureTask<V> implements RunnableFuture<V> {
......
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
//
//笔者注:jdk很多源码都使用这种循环的风格,每次循环只执行一个操作。例如创建一个等待节点入
//waiters链,分两次循环,第一次创建节点,第二次循环入waiters链。笔者认为这样做的好处是:
//该任务可能在“创建节点”和“入waiters链”中间完成,这样第二次循环就不是入waiters链而是直
//接返回。其它情况也是一样,任务的完成是异步的,随时可能在某个语句之后完成。这样写能让线
//程尽快返回。
//
for (;;) {
//
//笔者注:唤醒线程的原因如果是中断,则要抛中断异常 并且将当前线程从waiters链中移除
//
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)
//
//笔者注:如果等待节点没有入队,则入waiters链
//
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
//
//笔者注:如果设置了等待时间,且等待时间未到,则parkNanos阻塞线程一定的时间
//
LockSupport.parkNanos(this, nanos);
}
else
//
//笔者注:阻塞线程
//
LockSupport.park(this);
}
}
......
}
当任务完成,执行线程会遍历waiters链表,并唤起每个waiter对应的线程。源码如下所示(关键部分笔者添加了注释):
public class FutureTask<V> implements RunnableFuture<V> {
......
private void finishCompletion() {
// assert state > COMPLETING;
//
//笔者注:利用自旋抢到执行权,然后去遍历waiters链表
//
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
//
//笔者注:遍历waiters链表,唤醒对应等待该任务的线程
//
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
}
......
}
Demo
LockSuport各方法场景实验,demo代码如下:
public class LockSupportDemo {
private static Thread threadA1 = new Thread() {
public void run() {
System.out.println("A1: before park");
LockSupport.park();
System.out.println("A1: after unpark ");
}
};
private static Thread threadA2 = new Thread() {
public void run() {
//睡一小会 保证A1先执行
try {
Thread.currentThread().sleep(50);
}catch (InterruptedException e) {
System.out.println("A2 interrupted");
}
System.out.println("A2: before unpark A1");
try {
Thread.currentThread().sleep(2000);
}catch (InterruptedException e) {
System.out.println("A2 interrupted");
}
System.out.println("A2: 2s later");
}
};
/**
* A1 call park, then main call unpark(A1)
* 期望:
* A1: before park
* A2: before unpark A1
* A2: 2s later
* A1: after park
*/
protected static void testPark1() {
threadA1.start();
threadA2.start();
try {
Thread.currentThread().sleep(5000);
}catch (InterruptedException e) {
System.out.println("main interrupted");
}
LockSupport.unpark(threadA1);
}
private static Thread threadB1 = new Thread() {
public void run() {
//睡一小会 保证main可以先执行unpark
try {
Thread.currentThread().sleep(50);
}catch (InterruptedException e) {
System.out.println("B1 interrupted");
}
System.out.println("B1: before park");
LockSupport.park();
//LockSupport.park();
System.out.println("B1: after unpark ");
}
};
private static Thread threadB2 = new Thread() {
public void run() {
//比B1多睡50ms 保证B1先执行
try {
Thread.currentThread().sleep(100);
}catch (InterruptedException e) {
System.out.println("B2 interrupted");
}
System.out.println("B2: before unpark B1");
try {
Thread.currentThread().sleep(2000);
}catch (InterruptedException e) {
System.out.println("B2 interrupted");
}
System.out.println("B2: 2s later");
}
};
/**
* main call unpark(B1), then B1 call park
* 期望:
* B1: before park
* B1: after unpark
* B2: before unpark B1
* B2: 2s later
*/
protected static void testPark2() {
threadB1.start();
//C睡50ms 保证unpark先调用
LockSupport.unpark(threadB1);
//LockSupport.unpark(threadC);
threadB2.start();
}
private static Thread threadC1 = new Thread() {
public void run() {
System.out.println("C1: before park");
LockSupport.parkNanos(3000000000l);
System.out.println("C1: after unpark 3s later");
}
};
private static Thread threadC2 = new Thread() {
public void run() {
//比C1多睡50ms 保证C1先执行
try {
Thread.currentThread().sleep(50);
}catch (InterruptedException e) {
System.out.println("C2 interrupted");
}
System.out.println("C2: before unpark C1");
try {
Thread.currentThread().sleep(2000);
}catch (InterruptedException e) {
System.out.println("C2 interrupted");
}
System.out.println("C2: 2s later");
}
};
/**
* C1 call parkNanos(3000000), then main call unpark(C1) 5s later
* 期望:
* C1: before park
* C2: before unpark C1
* C2: 2s later
* C1: after unpark 3s later
* main: 5s later
*/
protected static void testParkNanos() {
threadC1.start();
threadC2.start();
try {
Thread.currentThread().sleep(5000);
}catch (InterruptedException e) {
System.out.println("main interrupted");
}
System.out.println("main: 5s later");
LockSupport.unpark(threadC1);
}
private static Thread threadD1 = new Thread() {
public void run() {
System.out.println("D1: before park");
//3s后
Long deadLine = System.currentTimeMillis() + 3000;
//过去的一个时间 则立即park不阻塞
//Long deadLine = System.currentTimeMillis() - 1000;
LockSupport.parkUntil(deadLine);
System.out.println("D1: after unpark 3s later");
}
};
private static Thread threadD2 = new Thread() {
public void run() {
//比D1多睡50ms 保证D1先执行
try {
Thread.currentThread().sleep(50);
}catch (InterruptedException e) {
System.out.println("D2 interrupted");
}
System.out.println("D2: before unpark D1");
try {
Thread.currentThread().sleep(2000);
}catch (InterruptedException e) {
System.out.println("D2 interrupted");
}
System.out.println("D2: 2s later");
}
};
/**
* D1 call parkUtil(currentTime + 3000), then main call unpark(D1) 5s later
* 期望:
* D1: before park
* D2: before unpark D1
* D2: 2s later
* D1: after unpark 3s later
* main: 5s later
*/
protected static void testParkUtil() {
threadD1.start();
threadD2.start();
try {
Thread.currentThread().sleep(5000);
}catch (InterruptedException e) {
System.out.println("main interrupted");
}
System.out.println("main: 5s later");
LockSupport.unpark(threadD1);
}
public static void main(String[] args) {
System.out.println("**************");
LockSupportDemo.testPark1();
//睡6s保证 下面test不影响前面的test输出
try {
Thread.currentThread().sleep(6000);
}catch (InterruptedException e) {
System.out.println("main interrupted");
}
System.out.println("**************");
LockSupportDemo.testPark2();
//睡6s保证 下面test不影响前面的test输出
try {
Thread.currentThread().sleep(6000);
}catch (InterruptedException e) {
System.out.println("main interrupted");
}
System.out.println("**************");
LockSupportDemo.testParkNanos();
//睡6s保证 下面test不影响前面的test输出
try {
Thread.currentThread().sleep(6000);
}catch (InterruptedException e) {
System.out.println("main interrupted");
}
System.out.println("**************");
LockSupportDemo.testParkUtil();
}
}
运行结果:
**************
A1: before park
A2: before unpark A1
A2: 2s later
A1: after unpark
**************
B1: before park
B1: after unpark
B2: before unpark B1
B2: 2s later
**************
C1: before park
C2: before unpark C1
C2: 2s later
C1: after unpark 3s later
main: 5s later
**************
D1: before park
D2: before unpark D1
D2: 2s later
D1: after unpark 3s later
main: 5s later
总结
总结一下LockSupport的使用要点:
- 线程调用park阻塞自己(类似sleep),直到中断、超时或者其它线程调用unpark;
- 线程调用park之前,如果其它线程调用了unpark,则会立即返回;
- 线程唤醒以后,线程并不知道是什么原因被唤醒,需要去判断时间、线程状态(Thread.interrupted());
- 线程start之前,unpark没有用,线程start之后park仍然会阻塞;
- 线程调用park阻塞不会释放线程占用的资源(锁);