理解了多线程,才能理解了现代分时操作系统,并进一步理解更多系统应用组件的底层原理。并发编程是低级程序员跨越到中高级程序员必过一关,是区分程序员水平的最常用的标准。在程序员求职过程中,多线程知识会被频繁问及考察,几乎所有的招聘要求里都会要求熟悉高并发编程。
1.wait需要配合while使用
在线程协调的场景中,常常会看到wait和while配合使用。
比如一个消费者,在判定队列是否为空,使用如下代码:
为什么需要使用到while呢?
public void synchronized consume() {
while(队列==空) {
wait();
}
消费();
notifyAll();
}
2.机制原理
我们逐行分析代码,
-
首先这是一个同步方法。synchronized意味着,方法在执行过程中会通过锁定this,实现互斥原子性操作。
-
此时,如果队列==空,那么意味着消费者线程需要等待,那么就会执行wait()方法。
-
wait()方法执行后,就会释放锁,并将当前线程放进等待队列中。
2.1 为什么需要再次判定
- 如果有两个生产者A和B,一个消费者C。当存储空间满了之后,生产者A和B都被wait,进入等待唤醒队列。当消费者C取走了一个数据后,如果调用了notifyAll(),则生产者线程A和B都将被唤醒,如果此时A和B中的wait不在while循环中而是在if中,**则A和B就不会再次判断是否符合执行条件,**都将直接执行wait()之后的程序,那么如果A放入了一个数据至存储空间,则此时存储空间已经满了;但是B还是会继续往存储空间里放数据,错误便产生了。
- 如果有两个生产者A和B,一个消费者C。当存储空间满了之后,生产者A和B都被wait,进入等待唤醒队列。当消费者C取走了一个数据后,如果调用了notify(),则A和B中的一个将被唤醒,假设A被唤醒,则A向存储空间放入了一个数据,至此空间就满了。**A执行了notify()之后,如果唤醒了B,**那么B不会再次判断是否符合执行条件,将直接执行wait()之后的程序,这样就导致向已经满了数据存储区中再次放入数据。
2.2 为什么需要使用while呢,能不能使用if 判断呢?
- 如果使用if
- 当这个沉睡的线程被唤醒时,它醒来的地方就会直接进入下一个指令
消费()
。而不会在此判断。
- 当这个沉睡的线程被唤醒时,它醒来的地方就会直接进入下一个指令
- 如果使用while
- 当这个沉睡的线程被唤醒时,它醒来的地方还在循环内。于是会进行再一次判定,队列是否为空。
- 如果队列为空就会继续wait()
- 如果队列不为空才会执行
消费()
。
总结
记住,在多线程开发模式里需要注意安排wait和while的搭档,他们两个能够形成一个安全屏障,让不该醒来却醒来的线程再次睡去。
多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。
https://github.com/forestnlp/concurrentlab
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。