经典的生产者消费者问题:
初级代码:通过改编读写线程程序得来。
class Goods{
private int count=1;
private String name;
boolean flag=false;
public synchronized void set(String name)throws InterruptedException{
if(flag){
this.wait();
}
this.name=name+"---"+count++;
System.out.println("生产者生产了"+this.name);
flag=true;
notify();
}
public synchronized void get()throws InterruptedException{
if(!flag)
this.wait();
System.out.println("消费者消费了--------"+name+":"+count);
flag=false;
notify();
}
}
class Producer implements Runnable{
private Goods g;
Producer(Goods g){
this.g=g;
}
public void run(){
while(true){
try{
g.set("+商品+");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private Goods g;
Consumer(Goods g){
this.g=g;
}
public void run(){
while(true){
try{
g.get();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
public class ProConsumDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Goods g=new Goods();
Producer pro=new Producer(g);
Consumer con=new Consumer(g);
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
Thread t3=new Thread(con);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
如果只有一个生产者一个消费者,运行结果是生产一个消费一个,但是如果有多个消费者多个生产者同时运行,运行结果如下:
这不符合生产一个消费一个的步骤,问题出在哪里呢?
该程序有四个线程,生产者1,生产者2,消费者3,消费者4,假设生产者1先抢到cpu,这时flag为false,继续执行生产代码,生产完后flag置为true,并且notify()一个线程,此时生产者1仍然有cpu的执行权,判断flag为true,这时wait了,这时活着的线程有生产者2,消费者3,和消费者4,假设生产者2获取到了cput的执行权,先判断flag,仍然为true,生产者2也wait(),这时假设消费者3抢到了cput执行权,判断flag,为false,执行消费代码,消费完后将flag置为false,并notify一个线程,因为生产者1先进入线程池,所以先唤醒生产者1,由于消费者3仍然有执行权,就回去判断flag,flag为false,所以消费者3wait(),这时活着的线程是生产者1和消费者4,假设消费者4先获取到cpu执行权,去判断flag,为false,所以也等待,这时生产者1得到cpu,由于只用if判断了一次flag,且生产者1停滞于wait()代码处,所以一开始执行就直接从wait之后执行生产代码,不再判断flag,生产完后,将flag置为true,notify一个线程,这时应该notify生产者2,此时生产者2也是直接从wait之后就开始执行生产代码,于是就产生了生产多个,消费一个的问题,同样道理也会出现生产一个消费多个的问题。
那么问题到底出在哪里呢?通过分析可以看出是因为唤醒了一个与上一个线程相同操作的线程,且该线程唤醒之后不再判断flag而是直接就开始执行之后代码,所以应该使用while循环来判断flag,但是如果只改while却可能会出现一直等待的情况,就是因为使用notify只能唤醒一个线程,如果唤醒一个与刚放弃cpu的线程相同操作的线程,那么这个线程又进入等待状态,那么所有的线程都进入等待状态。所以应该唤醒一个不同操作的线程,就需要用到notifyAll()。
改进代码:
class Goods{
private int count=1;
private String name;
boolean flag=false;
public synchronized void set(String name)throws InterruptedException{
while(flag){
this.wait();
}
this.name=name+"---"+count++;
System.out.println("生产者生产了"+this.name);
flag=true;
notifyAll();
}
public synchronized void get()throws InterruptedException{
while(!flag)
this.wait();
System.out.println("消费者消费了--------"+name+":"+count);
flag=false;
notifyAll();
}
}
class Producer implements Runnable{
private Goods g;
Producer(Goods g){
this.g=g;
}
public void run(){
while(true){
try{
g.set("+商品+");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private Goods g;
Consumer(Goods g){
this.g=g;
}
public void run(){
while(true){
try{
g.get();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
public class ProConsumDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Goods g=new Goods();
Producer pro=new Producer(g);
Consumer con=new Consumer(g);
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
Thread t3=new Thread(con);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
小小总结一下:
JDK升级成JDK1.5后提供了一些新特性就可以有效的解决生产者消费者问题。
提供了Lock接口和Condition对象,Lock用来替代synchronized,Condition对象的方法用来替代wait、notify、notifyAll方法。
代码实现:
import java.util.concurrent.locks.*;
class Goods2{
private int count=1;
private String name;
boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition condition_pro=lock.newCondition();
private Condition condition_con=lock.newCondition();
public void set(String name)throws InterruptedException{
lock.lock();
try{
while(flag){
condition_pro.await();
}
this.name=name+count++;
System.out.println("生产者生产了"+this.name);
flag=true;
condition_con.signal();
}
finally{
lock.unlock();
}
}
public void get()throws InterruptedException{
lock.lock();
try{
while(!flag)
condition_con.await();
System.out.println("消费者消费了"+this.name);
flag=false;
condition_pro.signal();
}
finally{
lock.unlock();
}
}
}
class Producer2 implements Runnable{
private Goods2 g;
Producer2(Goods2 g){
this.g=g;
}
public void run(){
while(true){
try{
g.set("+商品+");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class Consumer2 implements Runnable{
private Goods2 g;
Consumer2(Goods2 g){
this.g=g;
}
public void run(){
while(true){
try{
g.get();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
public class LockConditionDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Goods2 g=new Goods2();
Producer2 pro=new Producer2(g);
Consumer2 con=new Consumer2(g);
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
Thread t3=new Thread(con);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果: