park&unpark使用举例,线程内调用park方法即暂停,其它线程调用unpark方法指定线程进行唤醒,相当于无锁的简化版wait-notify.

  1. import java.util.concurrent.locks.LockSupport;
  2. public class Test22 {
  3. public static void main(String[] args) throws InterruptedException {
  4. Thread t1 = new Thread(()->{
  5. System.out.println("start");
  6. try {
  7. Thread.sleep(1);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. System.out.println("park....");
  12. LockSupport.park();
  13. },"t1");
  14. t1.start();
  15. Thread.sleep(2);
  16. System.out.println("unpark...");
  17. LockSupport.unpark(t1);
  18. }
  19. }

特点

与Object的wait&notify相比

  • wait,notify和notifyAll必须配合Object Monitor使用,即必须在同步块内使用,拿到锁才能调用方法,而unpark不必。
  • park&unpark是以线程为单位来【阻塞】和【唤醒】线程,而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
  • park&unpark可以先unpark,而wait&notify不能先notify

park&unpark原理

每个线程都有自己的一个Parker对象,由三部分组成_counter,_cond和_mutex
-调用park,就是要看需不需要停下来歇息

  • 如果备用干粮耗尽,那么钻进帐篷歇息
  • 如果备用干粮充足,那么不需停留,继续前进

-调用unpark,就好比令干粮充足

  • 如果这时线程还在帐篷,就唤醒让他继续前进
  • 如果这时线程还在运行,那么下次他调用park时,仅是消耗备用干粮,不需停留继续前进。注意因为背包空间有限,所以多次调用unpark仅会补充一份备用干粮。

详细说明:

1、调用park方法
image.png

  • 当前线程调用Unsafe.park()方法
  • 检查_counter值,如果_counter=0说明线程需要暂停,获得_mutex互斥锁,线程进入_cond条件变量阻塞并设置_counter=0.如果_counter=1说明线程无需暂停,只需令_counter=0即可。

2、调用unpark方法(线程已经调用park方法处于等待态)
image.png

  • 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
  • 唤醒_cond条件变量中的Thread_0
  • Thread_0恢复运行
  • 恢复运行后设置_counter为0

3、调用unpark方法(线程未调用park方法处于运行态)
image.png

  • 调用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.

_counter可以理解为线程运行的“干粮”