生产者与消费者(二十五)
生产者与消费者(二十五)
在多线程的开发中,如果生产者创建资源的速度很快,而消费者消费的速度慢,这时生产者就要去等待消费者,这样会影响到处理的效率,反之亦然。为了处理这样的场景,我们需要准备一个缓冲区,即一个池子,生产者生产好后将资源存在缓冲区中,当缓存区满了就停止生产,消费者直接从缓存区中获取资源,当缓存区空了就停止消费,通知生产者生产。
生产者与消费者一般有两种实现方式,一种是管程法(即创建缓存区的方式),另一种是信号灯法(设置一个标识位实现)
管程法
例如,我们想要实现一个生产者生产汉堡,消费者消费汉堡的场景。在这个场景中我们有四个对象,生产者、消费者、汉堡和缓存区。
生产者
//生产者
class Productor implements Runnable{
SynContainer synContainer;
public Productor(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
Hamburger hamburger = new Hamburger(i);
synContainer.push(hamburger);
System.out.println("生产了第"+hamburger.id+"个汉堡");
}
}
}
消费者
//消费者
class Customer implements Runnable{
SynContainer synContainer;
public Customer(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
Hamburger hamburder = synContainer.pull();
System.out.println("消费了第"+hamburder.id+"个汉堡");
}
}
}
汉堡
//汉堡
class Hamburger {
int id;
public Hamburger(int id) {
this.id = id;
}
}
缓存区
//缓存区
class SynContainer {
//一个可以存放10个汉堡的缓存区
Hamburger[] hamburgers = new Hamburger[5];
int count = 0;
public synchronized void push (Hamburger hamburger) {
//如果缓存区中数量为10,则不生产,同志消费者消费
if (count == hamburgers.length) {
try {
System.out.println("缓存区已满,停止生产");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果缓存区中汉堡数量为0,则生产汉堡
hamburgers[count] = hamburger;
count++;
this.notifyAll();
}
public synchronized Hamburger pull () {
//如果缓存区中汉堡数量为0,则停止消费,呼叫生产者生产
if (count == 0) {
try {
System.out.println("缓存区已空,停止消费");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果缓存区中汉堡数量>0,则消费汉堡
Hamburger hamburger = hamburgers[count-1];
count--;
this.notifyAll(); //已经消费了,通知生产者可以生产了
return hamburger;
}
}
Main方法
public class GuanCheng {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Productor productor = new Productor(container);
Customer customer = new Customer(container);
new Thread(productor).start();
new Thread(customer).start();
}
}
我们运行起来,看下结果,生产者将生产的汉堡存放在缓存区,消费者直接从缓存区中获取汉堡:
缓存区已空,停止消费
生产了第1个汉堡
生产了第2个汉堡
生产了第3个汉堡
生产了第4个汉堡
生产了第5个汉堡
生产了第6个汉堡
消费了第1个汉堡
缓存区已满,停止生产
消费了第6个汉堡
生产了第7个汉堡
消费了第7个汉堡
消费了第8个汉堡
生产了第8个汉堡
消费了第5个汉堡
消费了第9个汉堡
消费了第4个汉堡
消费了第3个汉堡
消费了第2个汉堡
缓存区已空,停止消费
生产了第9个汉堡
生产了第10个汉堡
消费了第10个汉堡
信号灯法
信号灯法就是设置一个标识位,当标识位flag位true时,进行生产;当标识位为false时,进行消费。
下面是一个红绿灯的例子,当标识位改变时,分别让行人和车辆通行。
package com.proandcum;
public class XinHao {
public static void main(String[] args) {
Road road = new Road();
new Thread(new Car(road)).start();
new Thread(new Walker(road)).start();
}
}
class Car implements Runnable{
Road rode;
public Car(Road rode) {
this.rode = rode;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
rode.drive();
}
}
}
class Walker implements Runnable{
Road road;
public Walker(Road road) {
this.road = road;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
road.walk();
}
}
}
class Road {
int carCount = 0;
int walkerCount = 0;
boolean flag = true;
public synchronized void drive () {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("通过了第"+(++carCount)+"辆车");
flag = !this.flag;
this.notifyAll();
}
public synchronized void walk () {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("通过了第"+(++walkerCount)+"个行人");
flag = !this.flag;
this.notifyAll();
}
}
下面是代码运行起来的结果:
通过了第1辆车
通过了第1个行人
通过了第2辆车
通过了第2个行人
通过了第3辆车
通过了第3个行人
通过了第4辆车
通过了第4个行人
通过了第5辆车
通过了第5个行人
通过了第6辆车
通过了第6个行人
通过了第7辆车
通过了第7个行人
通过了第8辆车
通过了第8个行人
通过了第9辆车
通过了第9个行人
通过了第10辆车
通过了第10个行人
可以看到信号灯的改变依次让行人和车辆通行。