线程间的通讯–生产者与消费者问题
1.1模式原理
它描述的是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者可以从仓库中取走产品,解决生产者/消费者问题,我们需要采用某种机制保护生产者和消费者之间的同步;
同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性,常用的方法就是加锁,保证资源在任意时刻只被一个线程访问;
2.1实现
采用wait()、notify()和notifyAll()方法
wait():当缓冲区已满或空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行
·是Object的方法
·调用方式:对象.wait();
·表示释放 对象 这个锁标记,然后在锁外边等待(对比sleep(),sleep是抱着锁休眠的)
·等待,必须放到同步代码段中执行
notify():当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态
·是Object的方法
·调用方式:对象.notify();
·表示唤醒 对象 所标记外边在等待的一个线程
notifyAll():全部唤醒
·是Object的方法
·调用方式:对象.notifyAll()
·表示唤醒 对象 所标记外边等待的所有线程
模拟生产面包和消费面包,实现代码:
/*
*面包
*/
public class Bread {
private int id;
private String productName;
public Bread() {}
public Bread(int id, String productName) {
super();
this.id = id;
this.productName = productName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
@Override
public String toString() {
return "Bread [id=" + id + ", productName=" + productName + "]";
}
}
/*
* 面包容器
*/
public class BreadCon {
private Bread con;
private boolean flag; //
//放入面包
public synchronized void input(Bread b){
while(flag==true){//使用while循环,保证线程在被唤醒后,需要再次判断条件.
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.con=b;
System.out.println(Thread.currentThread().getName()+"生产了"+b.getId());
flag=true;//更改锁标记
this.notifyAll();//避免死锁问题但此方法只有一个等待队列,每次唤醒所以线程,效率低;
}
//吃面包
public synchronized void output(){
while(flag==false){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Bread b=con;//取出面包
con=null;//取出面包后仓库就没有这个已经取出的面包了
System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+" 生产者:"+b.getProductName());
flag=false;
this.notifyAll();
}
}
/*
* 生产面包类
*/
public class Product implements Runnable{
private BreadCon con;
public Product(BreadCon con) {
this.con=con;
}
@Override
public void run() {
for(int i=1;i<=30;i++){
Bread b=new Bread(i, Thread.currentThread().getName());
this.con.input(b);
}
}
}
/*
* 消费面包
*/
public class Consume implements Runnable{
private BreadCon con;
public Consume(BreadCon con) {
this.con=con;
}
@Override
public void run() {
for(int i=1;i<=30;i++){
con.output();
}
}
}
public static void main(String[] args) {
//1创建容器
BreadCon con=new BreadCon();
//2生产
Product product=new Product(con);
//3消费
Consume consume=new Consume(con);
//4线程对象
Thread shaqiang=new Thread(product, "莎强");
Thread xiaocang=new Thread(consume,"小苍");
//5启动
shaqiang.start();
xiaocang.start();
}
//使用Jdk1.5 Lock优化生产者和消费者
package com.qf.day20_11;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 面包容器
* @author wgy
*
*/
public class BreadCon {
private Bread con;
private boolean flag;
private Lock lock=new ReentrantLock();
Condition proCondition=lock.newCondition();
Condition conCondition=lock.newCondition();
/**
* 放入面包
*/
public void input(Bread b) {
lock.lock();
try {
while (flag) {
try {
proCondition.await();
} catch (Exception e) {
// TODO: handle exception
}
}
con = b;
System.out.println(Thread.currentThread().getName() + "生产了" + b.getId() + "面包");
flag = true;
conCondition.signal();
} finally {
lock.unlock();
}
}
/**
* 消费面包
*/
public void output() {
lock.lock();
try {
while (!flag) {
try {
conCondition.await();
} catch (Exception e) {
// TODO: handle exception
}
}
Bread b = con;
System.out
.println(Thread.currentThread().getName() + "消费了" + b.getId() + "面包, 生产者名字:" + b.getProductName());
con = null;
flag = false;
proCondition.signal();
} finally {
lock.unlock();
}
}
}