park&unpark使用举例,线程内调用park方法即暂停,其它线程调用unpark方法指定线程进行唤醒,相当于无锁的简化版wait-notify.
import java.util.concurrent.locks.LockSupport;
public class Test22 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
System.out.println("start");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("park....");
LockSupport.park();
},"t1");
t1.start();
Thread.sleep(2);
System.out.println("unpark...");
LockSupport.unpark(t1);
}
}
特点
与Object的wait¬ify相比
- wait,notify和notifyAll必须配合Object Monitor使用,即必须在同步块内使用,拿到锁才能调用方法,而unpark不必。
- park&unpark是以线程为单位来【阻塞】和【唤醒】线程,而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
- park&unpark可以先unpark,而wait¬ify不能先notify
park&unpark原理
每个线程都有自己的一个Parker对象,由三部分组成_counter,_cond和_mutex
-调用park,就是要看需不需要停下来歇息
- 如果备用干粮耗尽,那么钻进帐篷歇息
- 如果备用干粮充足,那么不需停留,继续前进
-调用unpark,就好比令干粮充足
- 如果这时线程还在帐篷,就唤醒让他继续前进
- 如果这时线程还在运行,那么下次他调用park时,仅是消耗备用干粮,不需停留继续前进。注意因为背包空间有限,所以多次调用unpark仅会补充一份备用干粮。
详细说明:
1、调用park方法
- 当前线程调用Unsafe.park()方法
- 检查_counter值,如果_counter=0说明线程需要暂停,获得_mutex互斥锁,线程进入_cond条件变量阻塞并设置_counter=0.如果_counter=1说明线程无需暂停,只需令_counter=0即可。
2、调用unpark方法(线程已经调用park方法处于等待态)
- 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
- 唤醒_cond条件变量中的Thread_0
- Thread_0恢复运行
- 恢复运行后设置_counter为0
3、调用unpark方法(线程未调用park方法处于运行态)
- 调用Unsafe.unpark(Thread_0)方法,设置_counter值为1
- 下次线程再调用Unsafe.park()方法时,检查_counter=1,线程无需阻塞,继续运行
所以综上总结,park的时候检查_counter值,如果_counter值等于1,令_counter值为0并继续运行。如果_counter值等于0,表示线程需要暂停住,线程在此暂停。unpark可以理解为令_counter值为1,如果线程处于运行态(即之前没有调用过park方法),那么仅仅是令_counter值为1,线程继续运行。如果线程已经处于等待状态(已经调用了park方法),那么会首先令_counter值为1,然后唤醒线程使得线程恢复运行,线程恢复运行会将_counter置为0.