生产消费模型的应用场景
和线程有关的另一个话题——消费生产模型的设计。
具体应用场景——例如LOL里,自动产生兵线,吃掉兵线等。
一个更贴切的栗子:
在俄罗斯方块中,需要自动产生一些块,如果容器满了就无法产生,当有一行被完全填满就会消掉,这就很好地运用了生产消费模型。
生产消费模型设计
模型实现注意点:
- 阻塞线程使用方法:wait()
- 启动线程使用方法:notify()
- 因为有多个生产者、消费者读写仓库库存数据,因此会产生data race,需要将修改数据的操作锁起来。
- 锁的监听器对象应该和调用阻塞方法、启动线程方法的对象为同一个。因为是对于这个对象来说控制线程只有一个读写、哪一个应该被阻塞、哪一个应该被唤醒启动。
- 因此注意生产者、消费者的监听器应该传入的是同一个仓库对象。
遇到的一个问题:
可以看到在代码逻辑当中,在生产者线程中,当仓库满的时候有唤醒消费者线程的操作,但是这里不知道应该怎么写?直接传入消费者线程吗?先打一个问号。我想明白了补上。
wait()、wait(long timeoutMillis)、sleep()区别
我们知道线程的状态有new,runable,wait,timewaiting、blocked,dead几种,分别对应有调用方法。
当时区别过wait,blocked。
wait:阻塞后需要被唤醒,没有程序的执行权。
blocked:仍旧拥有执行权,只是没到分配的时间片而已。
- wait()方法:执行了wait方法的线程就被阻塞,丧失执行权。必须要另外的线程唤醒才可以继续执行。
- wait(long timeoutMillis):阻塞一段指定的时间之后被唤醒继续执行。在阻塞期间仍不拥有执行权。
- sleep(long millis)方法:阻塞一段时间后执行,阻塞期间一直拥有执行权。
代码
//Produce.java
public class Producer extends Thread{
private Warehouse warehouse ;
public Producer(Warehouse warehouse) {
this.warehouse = warehouse;
}
@Override
public void run() {
while(true){
synchronized (warehouse){
// 判断仓库是否满
if(warehouse.getOccupy()+1<= warehouse.capacity){
warehouse.notify();
warehouse.setOccupy(warehouse.getOccupy()+1);
System.out.println("生产者"+currentThread().getName()+" 生产1个产品, 仓库目前库存:"+warehouse.getOccupy());
}
else //满了
{
try {
System.out.println("仓库满了,停止生产。");
warehouse.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//Consumer.java
public class Consumer extends Thread{
private Warehouse warehouse;
public Consumer(Warehouse warehouse){
this.warehouse = warehouse;
}
public void run() {
while(true){
synchronized (warehouse){
// 判断仓库是否空
if(warehouse.getOccupy()-1>=0){
warehouse.notify();
warehouse.setOccupy(warehouse.getOccupy()-1);
System.out.println("消费者"+currentThread().getName()+" 消费1个产品, 仓库目前库存:"+warehouse.getOccupy());
}
else //空了
{
try {
System.out.println("仓库空了,停止消费。");
warehouse.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//Warehouse.java
public class Warehouse {
int capacity;
int occupy;
public Warehouse(int capacity) {
this.capacity = capacity;
this.occupy = 0;
}
public int getCapacity() {
return capacity;
}
public void setCapacity(int capacity) {
this.capacity = capacity;
}
public int getOccupy() {
return occupy;
}
public void setOccupy(int occupy) {
this.occupy = occupy;
}
}
//Manage.java
public class Manage {
public static void main(String[] args){
Warehouse warehouse = new Warehouse(1000);
Producer producer0 = new Producer(warehouse);
Producer producer1 = new Producer(warehouse);
Consumer consumer0 = new Consumer(warehouse);
Consumer consumer1 = new Consumer(warehouse);
producer0.start();
consumer0.start();
producer1.start();
consumer1.start();
}
}