应用场景:
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的的产品取走消费;
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,知道仓库中的产品被消费者取走位置;
如果仓库中放有产品,则消费者将产品取走消费,否则停止消费并等待,知道仓库中再次放入产品为止。
分析:这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
1.对于生产者,没有生产产品之前,要通知消费者等待。而生产产品之后,有需要马上通知消费者消费。
2.对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费
3.在生产者消费者问题中,仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
实现:两种方法
1.管程法
2.信号灯法
下面不多说,直接上代码!
代码一:
package com.thread;
/**
* 单队列阻塞模式
* @author Tim
*/
public class ProductorAndConsumer {
public static void main(String[] args) {
WareHouse wareHouse = new WareHouse();
Thread productor01 = new Thread(new Productor(wareHouse), "生产者1");
Thread productor02 = new Thread(new Productor(wareHouse), "生产者2");
Thread productor03 = new Thread(new Productor(wareHouse), "生产者3");
Thread consumer01 = new Thread(new Consumer(wareHouse), "消费者1");
Thread consumer02 = new Thread(new Consumer(wareHouse), "消费者2");
Thread consumer03 = new Thread(new Consumer(wareHouse), "消费者3");
productor01.start();
productor02.start();
productor03.start();
consumer01.start();
consumer02.start();
consumer03.start();
}
}
//生产者 生产馒头
class Productor implements Runnable {
WareHouse wareHouse;
public Productor(WareHouse wareHouse) {
this.wareHouse = wareHouse;
}
@Override
public void run() {
System.out.println("生产者开始生产");
for (int i = 0; i < 100; i++) {
wareHouse.push(new SteamedBun(Thread.currentThread().getName() + "-->" + (i + 1)));
}
System.out.println("生产者生产完毕");
}
}
//消费者 消费馒头
class Consumer implements Runnable {
WareHouse wareHouse;
public Consumer(WareHouse wareHouse) {
this.wareHouse = wareHouse;
}
@Override
public void run() {
System.out.println("消费者开始消费");
for (int i = 0; i < 100; i++) {
SteamedBun sb = wareHouse.pop();
System.out.println(Thread.currentThread().getName() + " " + sb + "个馒头被消费。");
}
System.out.print(Thread.currentThread().getName());
System.out.println("消费完毕");
}
}
class WareHouse { //仓库类 用来存放物资
private int pointer = 0;
private SteamedBun[] buf;
private int capacity;
public WareHouse() {
this(10);
}
public WareHouse(int capacity) {
this.capacity = capacity;
buf = new SteamedBun[capacity];
}
public synchronized void push(SteamedBun sb) {
while (pointer >= capacity) {//仓库没有空间了 此时生产者需要等待 仓库有空间后通知生产者继续生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//此时wait已经被解除,说明仓库有空闲位置了,所以可以把馒头放入仓库
buf[pointer++] = sb; //放入馒头 又有馒头了
this.notifyAll(); //通知所有消费者继续进行消费
}
public synchronized SteamedBun pop() {
SteamedBun sb = null;
while (pointer <= 0) {//仓库没有馒头了 此时消费者需要等待 仓库有馒头后通知消费者继续消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//此时wait已经被解除,说明仓库有馒头了,所以可以把馒头拿出消费
sb = buf[--pointer]; //消费馒头 又有空间了
this.notifyAll(); //通知所有生产者继续进行生产
return sb;
}
}
class SteamedBun {
String id;
public SteamedBun(String id) {
this.id = id;
}
@Override
public String toString() {
// StringBuilder stringBuilder = new StringBuilder("第");
// stringBuilder.append(id);
return id;
}
}
代码二
package com.thread;
/**
* 双队列阻塞模式
* @author Tim
*/
public class ProductorAndConsumer02 {
public static void main(String[] args) {
Syn syn = new Syn();
Thread productor01 = new Thread(new Product(syn),"生产者1");
Thread productor02 = new Thread(new Product(syn),"生产者2");
Thread consumer01 = new Thread(new Consume(syn),"消费者1");
Thread consumer02 = new Thread(new Consume(syn),"消费者2");
productor01.start();
productor02.start();
consumer01.start();
consumer02.start();
}
}
//生产者 生产馒头
class Product implements Runnable {
Syn wareHouse;
public Product(Syn wareHouse) {
this.wareHouse = wareHouse;
}
@Override
public void run() {
System.out.println("生产者开始生产");
for (int i = 0; i < 100; i++) {
wareHouse.push(new SteamedBun(Thread.currentThread().getName()+"-->"+(i+1)));
}
System.out.println("生产者生产完毕");
}
}
//消费者 消费馒头
class Consume implements Runnable {
Syn wareHouse;
public Consume(Syn wareHouse) {
this.wareHouse = wareHouse;
}
@Override
public void run() {
System.out.println("消费者开始消费");
for (int i = 0; i < 100; i++) {
SteamedBun sb = wareHouse.pop();
System.out.println(Thread.currentThread().getName() + " " + sb + "个馒头被消费。");
}
System.out.print(Thread.currentThread().getName());
System.out.println("消费完毕");
}
}
class Syn { //仓库类 用来存放物资
public int pointer = 0;
private SteamedBun[] buf;
private int capacity;
private Object productorQue = new Object();
private Object consumerQue = new Object();
public Syn() {
this(10);
}
public Syn(int capacity) {
this.capacity = capacity;
buf = new SteamedBun[capacity];
}
public void push(SteamedBun sb) {
synchronized (productorQue){
while (pointer >= capacity) {//仓库没有空间了 此时生产者需要等待 仓库有空间后通知生产者继续生产
try {
productorQue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (this){
//此时wait已经被解除,说明仓库有空闲位置了,所以可以把馒头放入仓库
buf[pointer++] = sb; //放入馒头 又有馒头了
}
}
synchronized (consumerQue){
consumerQue.notifyAll(); //通知所有消费者继续进行消费
}
}
public SteamedBun pop() {
SteamedBun sb=null;
synchronized (consumerQue){
while (pointer <= 0) {//仓库没有馒头了 此时消费者需要等待 仓库有馒头后通知消费者继续消费
try {
consumerQue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (this){
//此时wait已经被解除,说明仓库有馒头了,所以可以把馒头拿出消费
sb = buf[--pointer]; //消费馒头 又有空间了
}
}
synchronized (productorQue){
productorQue.notifyAll(); //通知所有生产者继续进行生产
}
return sb;
}
}
class Bun {
String id;
public Bun(String id) {
this.id = id;
}
@Override
public String toString() {
// StringBuilder stringBuilder = new StringBuilder("第");
// stringBuilder.append(id);
return id;
}
}
代码二和代码一的区别在于消费者线程和生产证线程是否阻塞在一个对象的等待队列中。