多线程—线程通信之notifyAll()/wait()方法Demo
多线程—线程通信之notifyAll()/wait()方法Demo
目标:实现在厨师做完20碗饭后,吃货吃一次,厨师做一次。以此达到20碗饭的动态平衡。
(1)厨师类:
package Test.ProducerAndCustomer;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//目标:复刻黑马程序员的厨师吃货Demo:
//厨师线程,吃货线程,list代表仓库,0代表没面条,1代表有面条
public class Producer implements Runnable{
Object object = Main.object;
@Override
public void run() {
while (true){
synchronized (object) {
if (Main.NoodleNum >= 20) {
try {
object.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
Main.NoodleNum++;
System.out.println("厨师已经做了" + Main.NoodleNum + "碗面条!");
//叫醒吃货
object.notifyAll();
}
}
}
}
}
(2)吃货类:
package Test.ProducerAndCustomer;
public class Customer implements Runnable{
@Override
public void run() {
Object object = Main.object;
while (true){
synchronized (object) {
if (Main.NoodleNum < 20) {
//唤醒厨师继续做:
try {
object.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
Main.NoodleNum--;
System.out.println("吃货现在吃了一碗面!");
object.notifyAll();
}
}
}
}
}
(3)主函数类:
package Test.ProducerAndCustomer;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
static int NoodleNum = 0;
static Object object = new Object();
public static void main(String[] args) {
Customer customer = new Customer();
Producer producer = new Producer();
Thread thCus = new Thread(customer,"吃货");
Thread thPro = new Thread(producer,"厨师");
thPro.start();
thCus.start();
}
}
最终运行效果:
注意:代码逻辑不难,主要在于把控notifyAll()和wait() 的动作时机,否则会出现,两个线程都进入等待状态却无人唤醒,导致程序卡死。
犯错点:
在确定同步代码块之前,尝试使用ReentrantLock的实例对象来给代码块加锁。在用这个实例对象调用.lock()/.unlock()方法后,又用它来调用wait(),notify()的方法。导致报出IlegalMonitorStateException的错误
这个错误的源头就是,使用lock()/unlock()等方法后又调用wait()/notify()。
因为后两者是隐式锁,而前两者是显式锁,正确的使用方式应该是使用synchronized()和wait(),notify()来搭配调用。
疑惑:
问:为什么synchronized和wait()/notify()的锁对象需要一致?
答:因为这是java对于线程通信的一种设计,如果前后不一致,则IDEA会如此说:
当前线程不是(锁的)拥有者。这也就表明,一个线程必须要先持有锁,才能进行wait()/notify()的操作。
联想:
1.如果想要套换到飞机大战的应用场景中,则会是 “保持屏幕当中的飞机数量为20,被打掉或者移出屏幕就会自动补全” 的效果。那到时候,上述代码中的NoodleNum就替换成敌机集合list,“厨师”则被替换成生成敌机的代码,吃货则被替换成我方飞机,而飞机的减少与补齐的行为则对应上述代码的对NoodelNum的增减操作,无非就是add()/remove()方法的套换。
2.在当今的社会生活中,生产者消费模型作为一种原理性的东西,首先会被应用到消息队列,在此基础上,订单_平台,数据采集_处理系统,图片视频处理,日志处理系统,无处不在,体现出它的广泛性。
生产者消费者模型的特点:
1.解耦性:生产者消费者彼此互不相识,互不干扰,彼此只关心并各自操作共用的那部分。
2.错误隔离:生产者出故障不影响消费者处理已有数据,消费者故障不影响生产者产生数据
食客(消费者)今天没来不代表厨师(生产者)今天不会做饭了,厨师今天打样不代表食客没地方吃饭
3.异步性:生产者和消费者的行为不具有强烈的顺序性和关联性,自由度高。
更多推荐
所有评论(0)