生产者消费者问题的java实现

生产者和消费者是多线程经典的问题,生产者和消费者问题的核心是同步的问题,同步问题的核心是要保证同一个资源被多个线程并发访问时的完整性,常用的方法是采用信号或加锁机制,保证资源在任一时刻只能被一个线程访问。这一问题用java来实现的话主要有4种方式。1.wait()/notify();2.await()/signal(); 3.blockingQuene 4.PipedInputStream/pipedOutputStream

下面分别来实现。

1.利用wait()和notify()来实现

Wait()方法:当缓冲区已空/满时,生产者/消费者停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。

Notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

下面看看代码实现:

首先定义商店类:

package ConsumerAndProducerProblem;

import java.util.LinkedList;

/**
 * @author: zhuwei
 * @ClassName: 商店类
 * @Description: TODO
 * @Date: 下午3:58:01
 */
public class Storage
{

    //定义仓库最大容量100
    private final int MAX_SIZE = 100;
    private LinkedList<Object> list= new LinkedList<>();

    //生产num个商品
    public void produce(int num) throws Exception
    {
       synchronized(list)
       {
           //假如仓库容量不足
           if(list.size()+num>MAX_SIZE)
           {
              System.out.println("仓储容量不足");

              //线程等待
              list.wait();
           }
           //仓库容量可以容量生产者的生产,则生产
           for(int i = 0;i < num;i++)
           {
              list.add(new Object());
           }
           System.out.println("生产者生产产品数量为:"+ num);
           list.notifyAll();
       }
    }

    //消费num个商品
    public void consume(int num) throws Exception
    {
       synchronized(list)
       {
           //加入仓库中的商品不能满足消费者的需求,线程等待
           if(list.size() < num)
           {
              System.out.println("仓库中的商品不能满足消费者需求");
              list.wait();
           }
           for(int i = 0;i < num;i++)
           {
              list.remove();
           }
           System.out.println("消费者消费商品数量为:"+num);
           list.notifyAll();
       }
    }
}
 

定义生产者类

package ConsumerAndProducerProblem;

/**
 * @author: zhuwei
 * @ClassName: 生产者线程
 * @Description: TODO
 * @Date: 下午3:57:15
 */
public class Consumer implements Runnable
{

    //消费商品数量
    private int number;

    private Storage storage;

    public void consume(int num)
    {
       try
       {
           storage.consume(num);
       } catch (Exception e)
       {
           // TODO Auto-generatedcatch block
           e.printStackTrace();
       }
    }

    public int getNumber()
    {
       return number;
    }

    public void setNumber(int number)
    {
       this.number = number;
    }

    public Storage getStorage()
    {
       return storage;
    }

    public void setStorage(Storage storage)
    {
       this.storage = storage;
    }

    @Override
    public void run()
    {
       // TODO Auto-generatedmethod stub
       consume(number);
    }

}
 

定义消费者类:

package ConsumerAndProducerProblem;

/**
 * @author: zhuwei
 * @ClassName: 消费者线程
 * @Description: TODO
 * @Date: 下午3:57:38
 */
public class Producer implements Runnable
{
    //生产的商品数量
    private int number;

    private Storage storage;

    public void produce(int num)
    {
       try
       {
           storage.produce(num);
       } catch (Exception e)
       {
           // TODO Auto-generatedcatch block
           e.printStackTrace();
       }
    }

    public int getNumber()
    {
       return number;
    }

    public void setNumber(int number)
    {
       this.number = number;
    }

    public Storage getStorage()
    {
       return storage;
    }

    public void setStorage(Storage storage)
    {
       this.storage = storage;
    }

    @Override
    public void run()
    {
       // TODO Auto-generatedmethod stub
       produce(number);
    }

}
 

创建测试类:

package ConsumerAndProducerProblem;

public class Test
{

    public static void main(String[] args)
    {
       // TODO Auto-generatedmethod stub
       //仓库对象
       Storage storage = new Storage();

       //消费者对象
       Consumer c1 = new Consumer();
       c1.setNumber(10);
       c1.setStorage(storage);
       Consumer c2 = new Consumer();
       c2.setNumber(80);
       c2.setStorage(storage);

       //生产者对象
       Producer p1 = new Producer();
       p1.setNumber(20);
       p1.setStorage(storage);
       Producer p2 = new Producer();
       p2.setNumber(50);
       p2.setStorage(storage);

       p1.run();
       c1.run();
       p2.run();
       c2.run();

    }

}

2.await()和signal()方法

该方法中用到的几个类做一下说明:

ReentrantLock():一个可重入的互斥锁Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。          ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用lock
的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread()和
getHoldCount()方法来检查此情况是否发生。

Condition():将 Object监视器方法(wait、notify和
notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock替代了
synchronized 方法和语句的使用,Condition替代了 Object 监视器方法的使用。

条件(也称为条件队列条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition()方法。

定义仓库的代码为:

package ConsumerAndProducerProblem2;

import java.util.LinkedList;
importjava.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;

public class Storage
{
         //定义仓库容量
         privatefinal int MAX_SIZE = 100;

         //
         privateLinkedList<Object> list = new LinkedList<>();

         //定义锁
         privateLock lock = new ReentrantLock();

         privateCondition full = lock.newCondition();

         privateCondition empty = lock.newCondition();

         publicint getMAX_SIZE()
         {
                   returnMAX_SIZE;
         }

         //生产商品
         publicvoid produce(int number) throws Exception
         {
                   //获得锁
                   lock.lock();
                   {
                            //加入仓库容量不能容纳生产者生产的商品,线程阻塞
                            while(list.size()+number> MAX_SIZE)
                            {
                                     System.out.println("仓库空间无法容量生产的商品");
                                     full.await();
                            }
                            for(inti = 0;i < number;i++)
                                     list.add(newObject());
                            System.out.println("生产者生产商品数量:"+number);
                            full.notifyAll();
                            empty.notifyAll();

                            //释放锁
                            lock.unlock();
                   }
         }

         //消费商品
         publicvoid consume(int number) throws Exception
         {
                   //获得锁
                   lock.lock();
                   //加入仓库的商品不能满足消费者消费需求
                   while(list.size()< number)
                   {
                            System.out.println("仓库中的商品不能满足消费者需求");
                            empty.wait();
                   }
                   for(inti = 0;i<number;i++)
                   {
                            list.remove();
                   }
                   System.out.println("消费者消费产品数量为:"+number);
                   full.notifyAll();
                   empty.notifyAll();

                   //释放锁
                   lock.unlock();

         }
}

生产者、消费者和测试类的代码保存不变

时间: 2024-12-19 00:49:55

生产者消费者问题的java实现的相关文章

生产者消费者问题、Java实现

来,今天尝试把这个问题搞定.还是这种节奏,看一个问题要先从历史看.全局看,这样我们才能真正掌握其全貌,最终各个击破,了然于胸! 我们先来温习下如下概念: 1. 基础概念 基本的 程序 - Program 程序是静态的源代码或目标程序,是一个没有生命的实体. 进程 - Process 当CPU赋予程序生命时也即操作系统执行它时,程序成为了一个活动的实体(但不是可执行的实体),称为进程 - 进行中的程序. 进程是程序的一个实例: 是计算机分配资源的基本单位: 是线程的容器: 线程 - Thread

生产者消费者模式的java实现(实现三)

Exchanger是java.util.concurrent类库下的一个并发工具.下面是java api文档里对Exchanger的描述. A synchronization point at which threads can pair and swap elements within pairs. Each thread presents some object on entry to the exchange method, matches with a partner thread, a

生产者消费者模式的java实现(实现二)

这次采用ReentrantLock 实现生产者消费者模式,先说下ReentrantLock,通常叫做重入锁,所谓重入就是一个线程可以再次进入已经持有锁的代码块,在内部会对重入的次数进行计数,当计数为0,则释放锁.其实synchronized关键字所代表的内置锁,也是可以重入的.但是又有几点不同: 1.ReentrantLock将加锁与解锁进行分离,可以提供更细粒度的加解锁操作,内置锁基本都是全局锁(类,对象,代码块) 2.ReentrantLock提供了定时的加锁操作,这是内置锁无法做到的. 3

生产者消费者模式的java实现(实现一)

在多线程以及并发工具类中,常用的一种思想就是生产者消费者模式,生产者负责生产物品,将物品放到传送带,消费者负责获取传送带的物品,消费物品.现在只考虑最简单的情况,传送带上只允许放一个物品. 1.传送带为空,则允许生产者放置物品,否则不许放(生产者线程wait). 2.生产者放置完物品后,通知消费者可以拿了(线程通信,notify 或者notifyAll). 2.传送带不空,则允许消费者拿物品,否则不许拿(消费者线程wait). 3.消费者拿走物品后,通知生产者可以继续生产(线程通信,notify

生产者-消费者问题【Java实现】

综合示例,演示有限长度字符序列缓冲区的并发读写, 或者称 生产者 - 消费者问题.错漏之处, 恳请指出 ^_^ /** * PCProblem : * 模拟生产者-消费者问题, 生产者产生字符并写入字符序列缓冲区, 消费者从缓冲区取走字符 * * @author shuqin1984 2011-08-05 * */ package threadprogramming.basic.simulation; import java.util.concurrent.ExecutorService; im

生产者——消费者模型的java代码实现

生产者 1 import java.util.Random; 2 3 4 public class Producer extends Thread { 5 6 private Storage<Product> storage; 7 8 public Producer(Storage<Product> storage) { 9 this.storage = storage; 10 } 11 12 @Override 13 public void run() { 14 produce(

生产者/消费者问题的Java实现

类似于Java阻塞队列的实现: public class BlockingQueue { private List queue = new LinkedList(); private int limit = 10;//缓冲区大小 public BlockingQueue(int limit){ this.limit = limit; } public synchronized void enqueue(Object item) throws InterruptedException { whil

java 多线程并发系列之 生产者消费者模式的两种实现

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据.同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者.为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式. 什么是生

Java并发之:生产者消费者问题

生产者消费者问题是Java并发中的常见问题之一,在实现时,一般可以考虑使用juc包下的BlockingQueue接口,至于具体使用哪个类,则就需要根据具体的使用场景具体分析了.本文主要实现一个生产者消费者的原型,以及实现一个生产者消费者的典型使用场景. 第一个问题:实现一个生产者消费者的原型. 1 import java.util.concurrent.*; 2 3 class Consumer implements Runnable { 4 BlockingQueue q = null; 5