1.park&unpark
1.1.概述
1>.他们是LockSupport类中的方法
// 暂停当前线程
LockSupport.park();
// 恢复某个线程的运行
LockSupport.unpark(暂停线程对象)
注意:先park再unpark!
1.2.案例
@Slf4j
public class TestPark {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.info("子线程t1 start...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("子线程t1 park...");
//子线程暂停在这一行
//获取对象锁的线程由于某些原因无法继续执行,线程变成WAITING状态
LockSupport.park();
log.info("子线程t1 resume");
}, "t1");
t1.start();
//main线程睡眠2s
Thread.sleep(2000);
log.info("main线程unPark t1线程");
//恢复某个被暂停都线程
LockSupport.unpark(t1);
}
}
注意:
当前程序中先在子线程中执行park()暂停子线程的执行,然后再在main线程中执行unpark()恢复暂停执行的线程,最终结果一切正常,但是如果是先在main线程中执行unpark()恢复某个暂停执行的线程,然后再在子线程中执行park()暂停子线程的执行,那么结果会怎样呢?答案是一切正常!
@Slf4j
public class TestPark {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.info("子线程t1 start...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("子线程t1 park...");
//子线程暂停在这一行
//获取对象锁的线程由于某些原因无法继续执行,线程变成WAITING状态
LockSupport.park();
log.info("子线程t1 resume");
}, "t1");
t1.start();
//main线程睡眠1s
Thread.sleep(1000);
log.info("main线程unPark t1线程");
//恢复某个被暂停都线程
LockSupport.unpark(t1);
}
}
结论:
LockSupport.unpark()
既可以在LockSupport.park()
之前执行,也可以在之后执行,效果都是一样的!
1.3.特点
1>.与Object的wait¬ify相比:
①.wait,notify/notifyAll必须配合Object Monitor(对象锁)一起使用,而park/unpark不必;
②.park&unpark是以线程为单位来"阻塞"和"唤醒"线程(即可以明确唤醒某个线程),而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么"精确";
③.park&unpark可以先unpark,而wait¬ify不能先notify;
1.4.原理
1>.每个线程都有自己的一个Parker对象,由三部分组成:_counter,_cond和_mutex;
打个比喻:
①.线程就像一个旅人,Parker就像他随身携带的背包,_cond(条件变量)就好比背包中的帐篷._counter就好比背包中的备用干粮(0为耗尽,1为充足),_mutex就是需要进帐篷休息;
②.调用park()就是要看需不需要停下来歇息:
- 如果备用干粮耗尽,那么钻进帐篷歇息;
- 如果备用干粮充足,那么不需停留,继续前进;
③.调用unpark(),就好比补充干粮:
- 如果这时线程还在帐篷,就唤醒让他继续前进(子线程首先执行park()处于暂停状态);
- 如果这时线程还在运行,那么下次他调park()时,仅是消耗掉备用干粮(unpark增加的备用干粮),不需停留而继续前进(先执行unpark(),然后子线程再执行park());
注意:因为背包空间有限,多次调用unpark()仅会补充一份备用干粮!
2>.原理图
①.当前线程Thread_0先调用Unsafe.park()方法;
②.检查_counter,本情况为0,这时,获得_mutex互斥锁;
③.当前线程Thread_0进入_cond条件变量阻塞(WAITING);
④.设置_counter=0;
①.其他线程之后再调用Unsafe.unpark(Thread_0)方法,设置_counter为1;
②.唤醒_cond条件变量中的Thread_0线程;
③.Thread_0线程恢复运行;
④.设置_counter为0;
①.其他线程先调用Unsafe.unpark(Thread_0)方法,设置_counter为1;
②.当前线程Thread_0调用Unsafe.park()方法;
③.检查_counter,本情况为1,这时线程Thread_0无需阻塞,继续运行;
④.最后设置_counter为0;