生产者与消费者问题的java示例

      生产者与消费者问题是线程同步问题中的一个经典案例,也称作有界缓冲区问题。生产者线程与消费者线程共享一个公共的固定大小的缓冲区。其中生产者将信息放入缓冲区,消费者从缓冲区中取出信息。但是问题在与当缓冲区已满,而生产者还想向其中放入新的数据时候如果不做处理的话就会导致新加入的数据越界。解决的办法是让生产者休眠一段时间,等待消费者从缓冲区中取出一个或者多个数据后再唤醒它/它们。同样的,当消费者试图从缓冲区取出数据而发现缓冲区为空时,消费者休眠一段时间,直达生产者向其中放入一些数据时再唤醒它/它们。
      这个不难理解,主要注意线程间的同步和处理好消费者/生产者被唤醒的时刻就可以了(不然的话可能导致生产者还没生产的产品就已经先被消费者吃掉的假现象,这是刚好生产者生产完但是还没输出信息时,线程切换到了消费者哪去了)。
import  java.util. * ;

public   class  ProducerConsumer
{
    
    
public static void main(String[] args)
    
{
        Can can 
= new Can(25);
        
        ThreadGroup tgPro 
= new ThreadGroup("Producer");   //生产者线程组
        can.setProducerGroup(tgPro);
        
        
new Thread(tgPro, new Producer(can, (int)(Math.random()*1000))).start();
        
new Thread(tgPro, new Producer(can, (int)(Math.random()*1000))).start();
        
new Thread(tgPro, new Producer(can, (int)(Math.random()*1000))).start();
        
new Thread(tgPro, new Producer(can, (int)(Math.random()*1000))).start();
        
new Thread(tgPro, new Producer(can, (int)(Math.random()*1000))).start();
        
        ThreadGroup tgCus 
= new ThreadGroup("Consumer");  //消费者线程组
        new Thread(tgCus, new Consumer(can, (int)(Math.random()*1000))).start();
        
new Thread(tgCus, new Consumer(can, (int)(Math.random()*1000))).start();
        
new Thread(tgCus, new Consumer(can, (int)(Math.random()*1000))).start();
        
        
    }
    
}


class  Can       // 用来装产品供生产者放产品和消费者去产品的篮子
{
    
private  ArrayList list = new ArrayList<String>();
    
private int max;
    
private ThreadGroup tgPro = null;

    Can(
int max)
    
{
        
this.max = max;
    }

    
    
public void push(String id)       //将产品放进篮子里
    {
        
synchronized(list)   //在list上加上对象锁
        {    
            
while(list.size() == 6)   //篮子的容量为6,当篮子满了的时候生产者
            {                          //必须等待消费者从篮子里取出产品。
                try
                
{
                    list.wait();
                }
catch(InterruptedException e)
                
{
                    
                    e.printStackTrace();
                }

                
            }

            
            list.add(id);        
//加入产品
//System.out.println("add one " + id);
        }

    }

        
    
public  String pop()            //从篮子里取出产品
    {
        String temp 
= null;
        
        
synchronized(list)          //同样在list上加上把锁,让线程同步
        {    
            
while(list.size() == 0)
            
{                
        
/*消费者在这地方要注意,1.如果篮子里没了东西了,它必须等待生产者装。
         *2.但它也不能一味地等,因为当生产者都死掉后,线程队列中可能还有
         * 很多个处于等待状态的消费者,这样将得不到唤醒,那可就天荒地老了。
         *所以要给它限定超时。 
*/
                    
                 
try                        
                
{                            
                    list.wait(
500);                 
                                     
                    
if(isEmpty())    //如果消费者醒来的时候,篮子里已经空了
                        return null;  //并且生产者都死掉了,那么只能返回null      
                                   
                }
catch(InterruptedException e)
                
{
                    e.printStackTrace();
                }

            }
    
                
            temp 
= (String)list.remove(list.size() - 1); //取出产品
            --max;            
//System.out.println("remove  one " + temp);                
            return temp;
        }
    
        
    }

    
    
public  boolean isEmpty()  
    
{
        
if( (tgPro.activeCount() == 0)//如果生产者都死掉了
              && (list.isEmpty())       //并且篮子里没东西了,那么
                  && (max == 0)  )        //意味篮子将永远空了。
           return true;
        
        
return false;
    }

    
    
public void notifyList()  //将唤醒作为一个同步方法独立出来,
    {                         //这样消费者或消费者在取出或者加入产品后在没唤醒
        synchronized(list)    //其他线程之前可以做点小动作。
        {
            list.notifyAll();
        }

    }

    
    
public void setProducerGroup(ThreadGroup tgPro)//设置消费者的线程组
    {
        
this.tgPro = tgPro;
    }

}



class  Producer  implements  Runnable    // 生产者类
{    
    
private static int ProducerID = 0;
    
private static int productID = 0;
    
private  int myId;
    
private Can can = null;
    
private int time;
    
    Producer(Can can, 
int time)
    
{
        
this.myId = ProducerID++;
        
this.can = can;
        
this.time = time;
    }

    
    
public void run()
    
{
        
for(int i=0; i<5; i++)
        
{
            can.push(
new String(""+myId + "" + productID));//加入产品,    
            System.out.println("Produce " + myId + " create: " + productID);//并且
            ++productID;                                      //做点小动作
        
            can.notifyList(); 
//唤醒其他线程,这样才不会出现生产者还没生产
                              
//但是已经被消费者吃掉的假现象,其实是生产者
                              
//已经生产了,但是还没输出信息的时候线程就被切换到
                              
//消费者那里去了。
            try
            
{
                Thread.sleep((
int)(Math.random() * time));//放大效果
                    
            }
catch(Exception e)
            
{
                e.printStackTrace();
            }

        }

            
System.out.println(
"--------------Producer " + myId + "closed----------------");    
    }

    
}


class  Consumer  implements  Runnable   // 消费者类
{    
    
private static int ConsumerID = 0;
    
private int myId;
    
private Can can = null;
    
private int time;
    
    Consumer(Can can, 
int time)
    
{
        
this.myId = ConsumerID++;
        
this.can = can;
        
this.time = time;
    }

        
    
public void run()
    
{
        String temp 
= null;
        
        
while(true)
        
{
            
if(can.isEmpty()) //如果篮子里没了,生产者死了,跳出循环,结束线程
            {
System.out.println(
"-------------------Consumer " + myId + "closed-----------");
                
break;
            }

            
else 
            
{        
                    temp 
= can.pop();
                    
if(temp == nullcontinue;    
                    System.out.println(
"Consumer " + myId + "  get: product" + temp);
                    
                    can.notifyList();
//唤醒其他线程        
            }

                
            
try
            
{            
                Thread.sleep((
int)(Math.random() * time));
                    
            }
catch(Exception e)
            
{
                e.printStackTrace();
            }

        }

    }

}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值