生产者消费者问题的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监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意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();
                  
         }
}

生产者、消费者和测试类的代码保存不变
相关文章
|
1月前
|
消息中间件 Java RocketMQ
MQ产品使用合集之在同一个 Java 进程内建立三个消费对象并设置三个消费者组订阅同一主题和标签的情况下,是否会发生其中一个消费者组无法接收到消息的现象
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
1月前
|
消息中间件 Java
Java操作RabbitMQ单一生产-消费者模式
Java操作RabbitMQ单一生产-消费者模式
37 0
|
1月前
|
消息中间件 存储 Java
Java与Go的生产者消费者模型比较
【4月更文挑战第20天】
23 1
|
1月前
|
Java
用java实现生产者和消费者模式
用java实现生产者和消费者模式
38 1
|
1月前
|
Java C++
Java实现信号量机制(生产者消费者问题)的三种方式
Java实现信号量机制(生产者消费者问题)的三种方式
52 0
|
1月前
|
Java
Java之多线程的生产者消费者问题的详细解析
3.生产者消费者 3.1生产者和消费者模式概述【应用】 概述 生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。
63 0
Java之多线程的生产者消费者问题的详细解析
|
1月前
|
存储 Java
Java实现生产者消费者案例
Java实现生产者消费者案例
31 0
时间轮-Java实现篇
在前面的文章《[时间轮-理论篇](https://developer.aliyun.com/article/910513)》讲了时间轮的一些理论知识,然后根据理论知识。我们自己来实现一个简单的时间轮。
|
3天前
|
Java 程序员
从菜鸟到大神:JAVA多线程通信的wait()、notify()、notifyAll()之旅
【6月更文挑战第21天】Java多线程核心在于wait(), notify(), notifyAll(),它们用于线程间通信与同步,确保数据一致性。wait()让线程释放锁并等待,notify()唤醒一个等待线程,notifyAll()唤醒所有线程。这些方法在解决生产者-消费者问题等场景中扮演关键角色,是程序员从新手到专家进阶的必经之路。通过学习和实践,每个程序员都能在多线程编程的挑战中成长。
|
3天前
|
Java
并发编程的艺术:Java线程与锁机制探索
【6月更文挑战第21天】**并发编程的艺术:Java线程与锁机制探索** 在多核时代,掌握并发编程至关重要。本文探讨Java中线程创建(`Thread`或`Runnable`)、线程同步(`synchronized`关键字与`Lock`接口)及线程池(`ExecutorService`)的使用。同时,警惕并发问题,如死锁和饥饿,遵循最佳实践以确保应用的高效和健壮。
10 2