Java编程生产者消费者实现的四种方法

目录
  • 实现生产者消费者的四种方式
    • 一、最基础的
    • 二、java.util.concurrent.lock 中的 Lock 框架
    • 三、阻塞队列BlockingQueue的实现
      • Blockqueue 接口的一些方法
    • 四、信号量 Semaphore 的实现

实现生产者消费者的四种方式

一、最基础的

利用 wait() 和 notify() 方法实现,当缓冲区满或为空时都调用 wait() 方法等待,当生产者生产了一个产品或消费者消费了一个产品后会唤醒所有线程;

package com.practice;
public class testMain {
    private  static  Integer count = 0;
    private  static  final Integer FULL = 10;
    private  static  String LOCK = "lock";
    public static void main(String[] args) {
        testMain testMain = new testMain();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
    }
    class Producer implements  Runnable{
        @Override
        public void run(){
            for (int i = 0; i < 10; i++) {
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }
                synchronized (LOCK){
                    while(count == FULL){//缓存空间满了
                        try{
                            LOCK.wait();//线程阻塞
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    count++;//生产者
                    System.out.println(Thread.currentThread().getName() + "生产者生产,目前总共有"+count);
                    LOCK.notifyAll();//唤醒所有线程
                }
            }
        }
    }
    class Consumer implements Runnable{
        @Override
        public void run(){
            for (int i = 0; i < 10; i++) {
                try{
                    Thread.sleep(3000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (LOCK){
                    while(count == 0){
                        try{
                            LOCK.wait();
                        }catch (Exception e){
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "消费者消费,目前总共有 "+count);
                    LOCK.notifyAll();//唤醒所有线程
                }
            }
        }
    }
}

二、java.util.concurrent.lock 中的 Lock 框架

通过对 lock 的 lock() 方法和 unlock() 方法实现对锁的显示控制,而 synchronize() 则是对锁的隐形控制,可重入锁也叫做递归锁,指的是同一个线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响;

简单来说,该锁维护这一个与获取锁相关的计数器,如果拥有锁的某个线程再次得到锁,那么获计数器就加1,函数调用结束计数器就减1,然后锁需要释放两次才能获得真正释放,已经获取锁的线程进入其他需要相同锁的同步代码块不会被阻塞

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest {
    private  static  Integer count = 0;
    private  static  Integer FULL = 10;
    //创建一个锁对象
    private Lock lock = new ReentrantLock();
    //创建两个条件变量,一个为缓冲非满,一个缓冲区非空
    private  final  Condition notFull = lock.newCondition();
    private  final  Condition notEmpty = lock.newCondition();
    public static void main(String[] args){
        ReentrantLockTest testMain = new ReentrantLockTest();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
    }
    class Producer implements Runnable{
        @Override
        public void run(){
            for (int i = 0; i <10; i++) {
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // 获取锁
                lock.lock();
                try {
                    while (count == FULL) {
                        try{
                            notFull.await();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    }
                    count++;
                    System.out.println(Thread.currentThread().getName()
                            + "生产者生产,目前总共有" + count);

            }finally {
                    lock.unlock();
                }
            }
        }
    }
    class Consumer implements Runnable{
        @Override
        public void run(){
            for (int i = 0; i <10; i++) {
                try{
                    Thread.sleep(3000);
                }
                catch (Exception e){
                    e.printStackTrace();
                }
                lock.lock();
                try{
                    while(count==0){
                        try{
                            notEmpty.await();
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() +
                            "消费者消费,目前总共有 " + count);
                }finally {
                    lock.unlock();//解锁
                }
            }

        }
    }
}

三、阻塞队列BlockingQueue的实现

被阻塞的情况主要分为如下两种,BlockingQueue 是线程安全的

1,当队列满了的时候进行入队操作;

2,当队列空的时候进行出队操作

Blockqueue 接口的一些方法

四类方法分别对应于:

1,ThrowsException,如果操作不能马上进行,则抛出异常;

2,SpecialValue 如果操作不能马上进行,将会返回一个特殊的值,true或false;

3,Blocks 操作被阻塞;

4,TimeOut 指定时间未执行返回一个特殊值 true 或 false

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
 * 使用 BlockQueue 实现生产者消费模型
 */
public class BlockQueueTest {
    public static  Integer count = 0;
    //创建一个阻塞队列
    final BlockingQueue blockingQueue = new ArrayBlockingQueue<>(10);
    public static void main(String[] args) {
        BlockQueueTest testMain = new BlockQueueTest();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
    }
    class Producer implements  Runnable{
        @Override
        public  void run(){
            for (int i = 0; i <10; i++) {
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }
                try{
                    blockingQueue.put(1);
                    count++;
                    System.out.println(Thread.currentThread().getName() + "生产者生产,目前总共有 " + count);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    class Consumer implements Runnable{
        @Override
        public void run(){
            for (int i = 0; i <10; i++) {
                try{
                    Thread.sleep(3000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                try{
                    blockingQueue.take();//消费
                    count--;
                    System.out.println(Thread.currentThread().getName() +
                            " 消费者消费,目前总共有 "+ count);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }

}

四、信号量 Semaphore 的实现

Semaphore (信号量) 用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。Java中的 Semaphone 维护了一个许可集,一开始设定这个许可集的数量,使用 acquire() 方法获得一个许可,当许可不足时会被阻塞,release() 添加一个许可。

下面代码中,还加入了 mutex 信号量,维护消费者和生产者之间的同步关系,保证生产者消费者之间的交替进行

import java.util.concurrent.Semaphore;
public class SemaphoreTest {
    private  static  Integer count = 0;
    //创建三个信号量
    final Semaphore notFull = new Semaphore(10);
    final Semaphore notEmpty = new Semaphore(0);
    final Semaphore mutex = new Semaphore(1);//互斥锁,控制共享数据的互斥访问
    public static void main(String[] args) {
        SemaphoreTest testMain = new SemaphoreTest();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
        new Thread(testMain.new Producer()).start();
        new Thread(testMain.new Consumer()).start();
    }
    class Producer implements Runnable{
        @Override
        public void run(){
            for (int i = 0; i <10; i++) {
                try{
                    Thread.sleep(3000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                try{
                    notFull.acquire();//获取一个信号量
                    mutex.acquire();
                    count++;
                    System.out.println(Thread.currentThread().getName() +
                            "生产者生产,目前总共有 "+count);
                } catch (InterruptedException e){
                    e.printStackTrace();
                } finally {
                    mutex.release();//添加
                    notEmpty.release();
                }
            }
        }
    }
    class Consumer implements  Runnable{
        @Override
        public void run(){
            for (int i = 0; i <10; i++) {
                try{
                    Thread.sleep(3000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                try{
                    notEmpty.acquire();
                    mutex.acquire();
                    count--;
                    System.out.println(Thread.currentThread().getName() +
                            "消费者消费,目前总共有"+count);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }finally {
                    mutex.release();
                    notFull.release();
                }
            }
        }
    }
}

Reference

https://juejin.cn/post/6844903486895865864#comment

以上就是Java编程生产者消费者实现的四种方法的详细内容,更多关于java实现生产消费者的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java多种方式实现生产者消费者模式

    实现需求:两个线程交替打印1,0,打印10轮 java多线程口诀: 高内聚,低耦合 线程操作资源类 判断干活通知 防止虚假唤醒 方式一:使用synchronized和Object的wait和notifyAll方法 wait:使当前线程阻塞 notify,notifyAll唤醒当前线程 /** * 两个线程交替打印1,0 打印10轮 * * @author Administrator * @version 1.0 2020年7月12日 * @see ProdConsumerDemo1 * @sin

  • 基于Java 生产者消费者模式(详细分析)

    生产者消费者模式是多线程中最为常见的模式:生产者线程(一个或多个)生成面包放进篮子里(集合或数组),同时,消费者线程(一个或多个)从篮子里(集合或数组)取出面包消耗.虽然它们任务不同,但处理的资源是相同的,这体现的是一种线程间通信方式. 本文将先说明单生产者单消费者的情况,之后再说明多生产者多消费者模式的情况.还会分别使用wait()/nofity()/nofityAll()机制.lock()/unlock()机制实现这两种模式. 在开始介绍模式之前,先解释下wait().notify()和no

  • Java实现简易生产者消费者模型过程解析

    一.概述 一共两个线程,一个线程生产产品,一个线程消费产品,使用同步代码块方法,同步两个线程.当产品没有时,通知生产者生产,生产者生产后,通知消费者消费,并等待消费者消费完. 需要注意的是,有可能出现,停止生产产品后,消费者还没未来得及消费生产者生产的最后一个产品,就结束消费,导致最后一个产品没有被消费. 本例使用synchronize以及wait().notify()实现简易版的线程者消费者模型. 二.测试用例 这里的产品用笔来演示,每只笔都有其编号code 一共有四个类:分别是生产者类,产品

  • Java生产者消费者模式实例分析

    本文实例讲述了Java生产者消费者模式.分享给大家供大家参考,具体如下: java的生产者消费者模式,有三个部分组成,一个是生产者,一个是消费者,一个是缓存. 这么做有什么好处呢? 1.解耦(去依赖),如果是消费者直接调用生产者,那如果生产者的代码变动了,消费者的代码也需要随之变动 2.高效,如果消费者直接掉生产者,执行时间较长的话,会阻塞,影响其他业务的进行 3.负载均衡,如果消费者直接调生产者,那生产者和消费者就得在一起了,日后业务量非常大的话,要想减轻服务器的压力,想拆分生产和消费,就很困

  • JAVA多线程实现生产者消费者的实例详解

    JAVA多线程实现生产者消费者的实例详解 下面的代码实现了生产者消费者的问题 Product.Java package consumerProducer; public class Product { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } public Product(String id) { this.id=id; } publ

  • Java编程生产者消费者实现的四种方法

    目录 实现生产者消费者的四种方式 一.最基础的 二.java.util.concurrent.lock 中的 Lock 框架 三.阻塞队列BlockingQueue的实现 Blockqueue 接口的一些方法 四.信号量 Semaphore 的实现 实现生产者消费者的四种方式 一.最基础的 利用 wait() 和 notify() 方法实现,当缓冲区满或为空时都调用 wait() 方法等待,当生产者生产了一个产品或消费者消费了一个产品后会唤醒所有线程: package com.practice;

  • Java编程求二叉树的镜像两种方法介绍

    给出一棵二叉树,求它的镜像,如下图:右边是二叉树是左边二叉树的镜像. 仔细分析这两棵树的特点,看看能不能总结出求镜像的步骤.这两棵树的根节点相同,但他们的左右两个子节点交换了位置.因此我们不妨先在树中交换根节点的两个子节点,就得到了下面一幅图中的第二颗树 解法1(递归) 思路1:如果当前节点为空,返回,否则交换该节点的左右节点,递归的对其左右节点进行交换处理. /*class TreeNode{ int val; TreeNode left=null; TreeNode right=null;

  • Android编程实现获取图片资源的四种方法

    本文实例讲述了Android编程实现获取图片资源的四种方法.分享给大家供大家参考,具体如下: 1. 图片放在sdcard中: 复制代码 代码如下: Bitmap imageBitmap = BitmapFactory.decodeFile(path)//path 是图片的路径,跟目录是/sdcard 2. 图片在项目的res文件夹下面 //得到application对象 ApplicationInfo appInfo = getApplicationInfo(); //得到该图片的id(name

  • java去除数组重复元素的四种方法

    方法1: 需要一个存储去重后元素的数组,然后两层循环,外层遍历原数组,内容逐个判断是否和之后的元素重复,然后再提出来存入新的数组.方法System.arraycopy(被复制的数组,起始下标,接收的新数组,起始下标,结束下标);可以直接复制数组,既然这样,我就没必要纠结新数组的长度了,直接声明一个跟原数组长度一样的临时数组,只要记录下去重后的元素个数,然后就可以确定去重后数组长度再去将临时数组拷贝到新数组就行了. package testPro.test; public class testDi

  • Java实现Map遍历key-value的四种方法

    目录 Map遍历key-value的四种方法 1.构造HashMap,给hashMap添加元素 2.方法一:先用keySet()取出所有key值,再取出对应value——使用迭代器遍历 3.方法二:先取出所有的key,再通过key取出对应的value——使用迭代器遍历 4.方法三:通过entrySet()获得key-value值——增强for循环遍历 5.方法四:通过entrySet()获得key-value值——使用迭代器遍历 6.完整代码 7.应用举例 Map遍历key-value的四种方法

  • Java添加事件监听的四种方法代码实例

    Java添加事件的几种方式(转载了codebrother的文章,做了稍微的改动): /** * Java事件监听处理--自身类实现ActionListener接口,作为事件监听器 * * @author codebrother */ class EventListener1 extends JFrame implements ActionListener { private JButton btBlue, btDialog; public EventListener1() { setTitle(

  • Java多线程——之一创建线程的四种方法

    1.实现Runnable接口,重载run(),无返回值 package thread; public class ThreadRunnable implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } package thread; public clas

  • java map遍历的四种方法总结

    整理了关于java中map的遍历的四种方法: import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;publicclassMapTest{privateMap<String,String> map;publicMapTest(){  map =newHashMap<String,String>();

  • Java实现生产者消费者问题与读者写者问题详解

    1.生产者消费者问题 生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品.解决生产者/消费者问题的方法可分为两类:(1)采用某种机制保护生产者和消费者之间的同步:(2)在生产者和消费者之间建立一个管道.第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式.第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强. 同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性.常

  • Java中生产者消费者问题总结

    生产者-消费者算是并发编程中常见的问题.依靠缓冲区我们可以实现生产者与消费者之间的解耦.生产者只管往缓冲区里面放东西,消费者只管往缓冲区里面拿东西.这样我们避免生产者想要交付数据给消费者,但消费者此时还无法接受数据这样的情况发生. wait notify 这个问题其实就是线程间的通讯,所以要注意的是不能同时读写.生产者在缓冲区满的时候不生产,等待:消费者在缓冲区为空的时候不消费,等待.比较经典的做法是wait和notify. 生产者线程执行15次set操作 public class Produc

随机推荐