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

 更新时间:2021年10月08日 10:27:21   作者:小张Python  
Java生产者和消费者问题是线程安全模型中的经典问题:生产者和消费者在同一个时间段共用同一个存储空间,生产者向存储空间中添加产品呢,消费者取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞

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

一、最基础的

利用 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 接口的一些方法

image-20210623112101617

四类方法分别对应于:

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实现生产消费者的资料请关注脚本之家其它相关文章!

相关文章

  • java操作excel导入导出的3种方式

    java操作excel导入导出的3种方式

    项目需要,要实现一个导入导出excel的功能,于是任务驱动着我学习到了POI、easypoi和easyexcel这3个java操作Excel的工具,下面这篇文章主要给大家介绍了关于java操作excel导入导出的3种方式,需要的朋友可以参考下
    2023-05-05
  • 超详细解析Spring Bean的创建过程

    超详细解析Spring Bean的创建过程

    这篇文章主要揭秘了Spring Bean的创建过程,文中通过代码示例和图文结合的方式解析的超级详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-05-05
  • 解决@RequestBody搭配@Data的大坑

    解决@RequestBody搭配@Data的大坑

    这篇文章主要介绍了解决@RequestBody搭配@Data的大坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Mybatis中如何使用sum对字段求和

    Mybatis中如何使用sum对字段求和

    这篇文章主要介绍了Mybatis中如何使用sum对字段求和,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • java中GZIP压缩解压类使用实例

    java中GZIP压缩解压类使用实例

    这篇文章主要介绍了java中GZIP压缩解压类使用实例的相关资料,需要的朋友可以参考下
    2017-03-03
  • java之lombok的构建者模式Builder中的泛型写法说明

    java之lombok的构建者模式Builder中的泛型写法说明

    这篇文章主要介绍了java之lombok的构建者模式Builder中的泛型写法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • Sentinel熔断规则原理示例详解分析

    Sentinel熔断规则原理示例详解分析

    这篇文章主要介绍了Sentinel熔断规则,采用了示例代码的方式对Sentinel熔断规则进行了详细的分析,以便广大读者朋友们更易理解,有需要的朋友可以参考下
    2021-09-09
  • 浅析如何在SpringBoot中实现数据脱敏

    浅析如何在SpringBoot中实现数据脱敏

    脱敏是指在不改变原数据结构的前提下,通过某种方式处理数据,使数据不能直接暴露用户的真实信息,下面我们就来看看SpringBoot中实现数据脱敏的具体方法吧
    2024-03-03
  • SpringMVC中处理Http请求的原理详解

    SpringMVC中处理Http请求的原理详解

    这篇文章主要介绍了SpringMVC中处理Http请求的原理详解,当一个http请求过来了首先经过的是DispatcherServlet这么一个前端控制器并调用了这个前端控制器的doService方法,这个方法最终我们发现它调用了doDispatcher这么一个方法,需要的朋友可以参考下
    2023-12-12
  • Java 文件传输助手的实现(单机版)

    Java 文件传输助手的实现(单机版)

    这篇文章主要介绍了Java 文件传输助手的实现(单机版),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05

最新评论