生产者消费者模式:生产者和消费者要处理同一货物对象,货物对象的属性对生产者和消费者的行为应该产生制约,即有货物才能消费,没有货物就进行生产。通过同步方法,wait()休眠线程,和notify或者notifyall唤醒线程来实现这个逻辑,详情见下面代码:
货物:
package com.way.threads.consumerandproducer;
public class Goods {
private String name;
private int num=0;
private boolean havegoods=false;//用来标记是否有货物的flag
public synchronized void produce(String name){
if(havegoods){
try {
this.wait();//等待其他进程运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name;
this.num++;
System.out.println("Produce "+name+" num: "+num);
this.havegoods=true;//设置flag
this.notify(); //唤醒线程
}
public synchronized void consume(){
if(!havegoods){
try {
this.wait();//等待其他进程运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.num--;
System.out.println("Consume "+name+" num: "+num);
this.havegoods=false;
this.notify(); //唤醒线程
}
}
生产者
package com.way.threads.consumerandproducer;
public class Producer implements Runnable{
//生产者,负责生产货物
private Goods goods;
public Producer(Goods goods){
super();
this.goods=goods;
}
public void run() {
for(int i=0;i<20;i++){
if(i%2==0){
goods.produce("bike");
}else{
goods.produce("car");
}
}
}
}
消费者
package com.way.threads.consumerandproducer;
public class Consumer implements Runnable{
//消费者,负责消费货物
private Goods goods;
public Consumer(Goods goods){
super();
this.goods=goods;
}
public void run() {
for(int i=0;i<20;i++){
goods.consume();
}
}
}
测试
package com.way.threads.consumerandproducer;
public class App {
public static void main(String[] args){
Goods goods=new Goods();
//生产消费同时进行,必须保证有商品的时候才能被消费
Consumer consumer=new Consumer(goods);
Producer producer=new Producer(goods);
new Thread(producer).start();
new Thread(consumer).start();
}
}
这个实现是货物只能存在一个,消费一个生产一个,实际也可以可以同时存在多个,那也就变成了“池”,可以对代码进行修改实现。代码如下:
货物:
package com.way.threads.consumerandproducer;
public class Goods {
private int num=0;
public synchronized void produce(){
if(isFull()){
try {
this.wait();//等待其他进程运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!isFull()){
this.num++;
System.out.println(Thread.currentThread().getName().toString()+" Produce "+" num: "+num);
try {
wait(500);//这里是为了让效果明显,不然太快了,看不出线程的切换
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notifyAll();
}
public synchronized void consume(){
if(isEmpty()){
try {
this.wait();//等待其他进程运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!isEmpty()){
this.num--;
System.out.println(Thread.currentThread().getName().toString()+" Consume "+" num: "+num);
try {
wait(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notifyAll();
}
public boolean isEmpty(){
if(num<=0){
return true;
}else{
return false;
}
}
public boolean isFull(){
if(num>=10){
return true;
}else{
return false;
}
}
}
生产者
package com.way.threads.consumerandproducer;
public class Producer implements Runnable{
//生产者,负责生产货物
private Goods goods;
public Producer(Goods goods){
super();
this.goods=goods;
}
public void run() {
for(int i=0;i<100;i++){
goods.produce();
}
}
}
消费者
package com.way.threads.consumerandproducer;
public class Consumer implements Runnable{
//消费者,负责消费货物
private Goods goods;
public Consumer(Goods goods){
super();
this.goods=goods;
}
public void run() {
for(int i=0;i<100;i++){
goods.consume();
}
}
}
测试
package com.way.threads.consumerandproducer;
public class App {
public static void main(String[] args){
Goods goods=new Goods();
//生产消费同时进行,必须保证有商品的时候才能被消费
Consumer consumer=new Consumer(goods);
Consumer consumer2=new Consumer(goods);
Consumer consumer3=new Consumer(goods);
Producer producer=new Producer(goods);
Producer producer2=new Producer(goods);
Producer producer3=new Producer(goods);
new Thread(producer).start();
new Thread(consumer).start();
new Thread(consumer2).start();
new Thread(producer2).start();
new Thread(consumer3).start();
new Thread(producer3).start();
}
}
这里启动了多个生产者和消费者线程,为了更好地展示效果。
部分结果:
Thread-1 Consume num: 2
Thread-3 Produce num: 3
Thread-5 Produce num: 4
Thread-4 Consume num: 3
Thread-2 Consume num: 2
Thread-0 Produce num: 3
Thread-2 Consume num: 2
Thread-4 Consume num: 1
Thread-5 Produce num: 2
Thread-3 Produce num: 3
Thread-1 Consume num: 2
Thread-3 Produce num: 3
Thread-5 Produce num: 4
Thread-4 Consume num: 3
Thread-2 Consume num: 2
Thread-0 Produce num: 3
Thread-2 Consume num: 2
可以发现,生产和消费在持续进行着
其他实现方式:
java中实现生产者消费者模式:
最简单的方式是基于Object类的wait()/notify()方法实现:
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
第二种方式是使用Lock的lock和unlock方法以及Condition相关的await()/signal()方法
使用与wait()/notify()类似
无论是生产者还是消费者,在lock()对“货物”加锁后,根据“容量”条件,使用await()阻塞线程,使用signal()唤醒其他等待线程,最后使用unlock()释放锁。
还可以使用BlockingQueue阻塞队列方法:
BlockingQueue是一个内部实现了同步机制的类,调用其put()和take()方法可以轻松实现生产者消费者模式:
put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。
take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。