《重新学习多线程》-- park,unpark 以及线程状态间的切换

527 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

park,unpark

基本使用

它们是 LockSupport 类中的方法

// 暂停当前线程

LockSupport.park();

// 恢复某个线程的运行

LockSupport.unpark(暂停线程对象)

先 park 再 unpark

carbon (17).png 先 unpark 再 park

carbon (16).png

原理

每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex 打个比喻

  • 线程就像一个旅人,Parker 就像他随身携带的背包,条件变量就好比背包中的帐篷。_counter 就好比背包中的备用干粮(0 为耗尽,1 为充足)

  • 调用 park 就是要看需不需要停下来歇息

    • 如果备用干粮耗尽,那么钻进帐篷歇息

    • 如果备用干粮充足,那么不需停留,继续前进

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

    • 如果这时线程还在帐篷,就唤醒让他继续前进

    • 如果这时线程还在运行,那么下次他调用 park 时,仅是消耗掉备用干粮,不需停留继续前进

      • 因为背包空间有限,多次调用 unpark 仅会补充一份备用干粮

image.png

  1. 当前线程调用 Unsafe.park() 方法

  2. 检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁

  3. 线程进入 _cond 条件变量阻塞

  4. 设置 _counter = 0

image.png

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1

  2. 唤醒 _cond 条件变量中的 Thread_0

  3. Thread_0 恢复运行

  4. 设置 _counter 为 0

image.png

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1

  2. 当前线程调用 Unsafe.park() 方法

  3. 检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行

  4. 设置 _counter 为 0

线程状态切换

image.png

假设有线程 Thread t

情况 1 NEW --> RUNNABLE

  • 当调用 t.start() 方法时,由 NEW --> RUNNABLE

情况 2 RUNNABLE <--> WAITING

t 线程用 synchronized(obj) 获取了对象锁后

  • 调用 obj.wait() 方法时,t 线程从 RUNNABLE --> WAITING

  • 调用obj.notify()obj.notifyAll()t.interrupt()

    • 竞争锁成功,t 线程从 WAITING --> RUNNABLE

    • 竞争锁失败,t 线程从 WAITING --> BLOCKED

情况 3 RUNNABLE <--> WAITING

  • 当前线程调用 t.join() 方法时,当前线程从 RUNNABLE --> WAITING

    • 注意是当前线程在t 线程对象的监视器上等待
  • t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING --> RUNNABLE

情况 4 RUNNABLE <--> WAITING

  • 当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING

  • 调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,会让目标线程从 WAITING -->RUNNABLE

情况 5 RUNNABLE <--> TIMED_WAITING

t 线程用 synchronized(obj) 获取了对象锁后

  • 调用 obj.wait(long n) 方法时,t 线程从 RUNNABLE --> TIMED_WAITING

  • t 线程等待时间超过了 n 毫秒,或调用 obj.notify()obj.notifyAll()t.interrupt()

    • 竞争锁成功,t 线程从 TIMED_WAITING --> RUNNABLE

    • 竞争锁失败,t 线程从 TIMED_WAITING --> BLOCKED

情况 6 RUNNABLE <--> TIMED_WAITING

  • 当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING

    • 注意是当前线程在t 线程对象的监视器上等待
  • 当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从TIMED_WAITING --> RUNNABLE

情况 7 RUNNABLE <--> TIMED_WAITING

  • 当前线程调用 Thread.sleep(long n) ,当前线程从 RUNNABLE --> TIMED_WAITING

  • 当前线程等待时间超过了 n 毫秒,当前线程从 TIMED_WAITING --> RUNNABLE

情况 8 RUNNABLE <--> TIMED_WAITING

  • 当前线程调用 LockSupport.parkNanos(long nanos)LockSupport.parkUntil(long millis) 时,当前线程从 RUNNABLE --> TIMED_WAITING

  • 调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,或是等待超时,会让目标线程从TIMED_WAITING--> RUNNABLE

情况 9 RUNNABLE <--> BLOCKED

  • t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败,从 RUNNABLE --> BLOCKED

  • 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争成功,从 BLOCKED --> RUNNABLE ,其它失败的线程仍然 BLOCKED

情况 10 RUNNABLE <--> TERMINATED

当前线程所有代码运行完毕,进入 TERMINATED