目录
park&unpark
wait,notify 和 park,unpark的区别
park unpark 原理
先调用park的情况
先调用park,在调用unpark的情况
先调用unpark,在调用park的情况
park&unpark
park和unpark都是LockSupport的方法,park用于暂停当前线程的运行,而unpark用于恢复该线程的运行.
我们看一下代码,来看看park和unpark怎么用?
@Slf4j(topic = "c.TestParkUnPark")
public class TestParkUnPark {
public static void main(String[] args) throws InterruptedException {
/**
* 我们这个unpark既可以在park之前调用,也可以在park之后调用
* 用来恢复暂停线程的运行
*/
Thread t1 = new Thread(() -> {
log.debug("开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("park");
LockSupport.park();//暂停当前线程
log.debug("resume...");
},"t1");
t1.start();
Thread.sleep(1000);
log.debug("unPark");
LockSupport.unpark(t1);//恢复t1线程的运行
}
}
先调用park,在调用unpark
先调用unpark在调用park
从这里我们就可以发现与wait和notify不同的一点,就是unpark在park之前调用还是在park之后调用都能唤醒该线程,而在使用wait和notify的时候,先调用notify,后调用wait,那么不会唤醒waitSet中的线程,应为已经错过通知的时间了,不会唤醒.
这里我总结一下wait,notify 和 park,unpark的区别
wait,notify 和 park,unpark的区别
- wait,notify是Object的方法,而park,unpark是LockSupport的方法
- wait,notify必须北河 Object Monitor一起使用,而park,unpark不需要
- park&unpark是以线程为单位来[阻塞]和[唤醒]线程的,而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,不那么精确.
- park &unpark 可以先调用unpark,而wait,notify不能先notify.
park unpark 原理
每一个线程都有一个parker对象,由三部分组成_counter(标志位),_condition(等待队列),_mutex(对象里面有等待队列)
我们可以打一个比喻.我们把线程当做一个旅游的人,Parker就像他随身携带的背包,条件变量就相当于背包中的帐篷,_counter就相当于背包中的备用干粮
- 调用park 就是要看当前的旅人要不要停下来去休息
-
- 如果备用干粮充足(counter为1)那么就不需要停留,继续前进
- 如果备用干粮耗尽(counter为0)那么就需要去condition帐篷(等待队列)休息
- 调用unpark,就相当于让干粮(counter重置为1)充足
-
- 如果这时旅人还在帐篷中(等待队列condition),那么就唤醒他继续前进
- 如果这时旅人还在前进,当下次调用park的时候,不休息,仅仅消耗干粮(counter'重置0),不需停留继续前进.
-
-
- 背包容量有效,多次调用unpark只会补充一份备用干粮
-
先调用park的情况
先调用park,在调用unpark的情况
先调用unpark,在调用park的情况
总结一下 :
- 如果调用的是park,先会去检查counter
-
- 如果counter为0,那么该线程获得互斥锁mutex,去mutex锁对象中的队列_cond中阻塞等待,再将counter置为0
- 如果counter为1,那么线程无需阻塞,线程继续运行,然后将counter置为0
- 如果调用的是unpark
-
- 如果counter为0,就会将counter置为1,然后唤醒在等待队列中阻塞的线程
- 如果此时线程还在继续运行,当下次在调用park的时候,检查counter为1,无需停留,继续运行,然后将counter置为0
另外注意 ,counter只有两种状态0,1多次调用unpark还是为状态还是为1
参考 : 黑马程序员 Java并发编程