一、Park & Unpark
1.1 基本使用
它们是 LockSupport 类中的方法
// 暂停当前线程
LockSupport.park();
// 恢复某个线程的运行
LockSupport.unpark(暂停线程对象)
先 park 再 unpark
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.LockSupport;
import static cn.itcast.n2.util.Sleeper.sleep;
@Slf4j(topic = "c.TestParkUnpark")
public class TestParkUnpark {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("start...");
sleep(1);
log.debug("park...");
LockSupport.park();
log.debug("resume...");
}, "t1");
t1.start();
// 主线程2s后调用unpark方法
sleep(2);
log.debug("unpark...");
LockSupport.unpark(t1);
}
}
运行结果:(t1此时处于无时限的等待状态…)
先 unpark再 park
unpark既可以在park之前调用也可在park之后调用
特点
与 Object 的 wait & notify 相比
● wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
● park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
● park & unpark 可以先 unpark,而 wait & notify 不能先 notify
1.2 Park & Unpark原理
每个线程都有自己的一个Parker对象(底层由C++代码实现),由三部分组成_counter, _cond 和_mutex 。打个比喻
● 线程就像一个旅人,Parker就像他随身携带的背包,条件变量就好比背包中的帐篷。_counter就好比背包中的备用干粮(0 为耗尽,1为充足)
● 调用park就是要看需不需要停下来歇息
—— 如果备用干粮耗尽,那么钻进帐篷歇息
—— 如果备用干粮充足,那么不需停留,继续前进
● 调用unpark, 就好比令干粮充足
—— 如果这时线程还在帐篷,就唤醒让他继续前进
—— 如果这时线程还在运行,那么下次他调用park时,仅是消耗掉备用干粮,不需停留继续前进
————因为背包空间有限,多次调用unpark仅会补充一份备用干粮
① 先park 再 unpark
● 当前线程调用Unsafe.park()
● 检查_counter,本情况为0,这时获得_mutex互斥锁
● 线程进入_cond条件变量阻塞
● 设置_cond=0
② unpark再 park
● 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
● 当前线程调用Unsafe.park()
● 检查_counter,本情况为1,这时线程无需阻塞,继续运行
● 设置_cond=0
1.3 interrupt-打断Park线程
打断 park 线程, 不会清空打断状态
Park线程
:不是Thread中的方法, 是LockSupport工具类中的方法,其作用也是使当前线程停下来
private static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
sleep(1);
t1.interrupt();
}
运行结果:调用park()后线程不会继续向下运行,使用interrupt()打断处在park状态的线程后此时线程会继续向下运行
注意:打断标记为真的情况下,再次park会失效
如何使其park后还能再次停止下来?
可将打断标记置为假(使用Thread.interrupted(),其会将打断标记清除,置为假)
二、线程状态转换
从Java层面线程状态分为六种
假设有线程 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
同时唤醒t1、t2线程,锁上的Owner只有一个,因此t1、t2只有一个线程成为Owner(一个竞争锁成功,一个竞争锁失败)
@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
final static Object obj = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (obj) {
log.debug("执行....");
try {
// 让线程t1在obj上一直等待下去
obj.wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其它代码....");
}
},"t1").start();
new Thread(()->{
synchronized (obj) {
log.debug("执行");
// 让线程t2在obj上一直等待下去
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t2").start();
// 主线程0.5秒后执行唤醒线程
sleep(0.5);
log.debug("唤醒 obj 上其它线程");
// 进入同一个对象中的Monitor
synchronized (obj) {
obj.notifyAll();
}
}
}
情况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 线程用 synchronized(obj) 获取了对象锁后
● 当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING
—— 注意是当前线程在t 线程对象的监视器上等待
t 线程用 synchronized(obj) 获取了对象锁后
● 当前线程等待时间超过了 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