Java并发编程(11)-使用ReentrantLock与Condition实现生产者-消费者队列

自己动手实现一个阻塞队列--ReentrantLock使用小结 背景 前几天看到一道面试题:实现一个阻塞队列,就萌生了动手操作一把的想法。看着挺简单的,思路也和清晰,就是用ReentantLock和Condition来实现,但在实际操作过程中还是遇到了问题,总结一下,仅供参考。 阻塞队列第一版 先附上第一版的代码。内部存储,为了方便就使用LinkedList来实现了。 import java.util.LinkedList; import java.u... 阅读详情


更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页


Condition在Java并发编程中是一个十分常用的接口,被称为条件变量,常与显式锁和可重入锁一起使用,它可以在某些条件下使线程进行休眠或者唤醒,本文将以实现生产者-消费者模式的队列作为demo让大家对条件变量 有初步的了解。

一、并发编程中的条件变量

1.1、从生产者-消费者模型理解条件变量

生产者-消费者模型是一种常见的模型,在《Java并发编程实践》中有一个例子很好地解释了这种模式:

两个洗盘子的劳动分工也是一个与生产者-消费者设计相类似的例子:一个人洗盘子,并把洗好的盘子放到盘子架上,另一个人从盘子架上得到盘子,并把它烘干。在这个场景中,盘子架充当了阻塞队列;如果架子上没有盘子,消费者会一直等待,直到有盘子需要烘干,如果盘子架被放满了,那么生产者会停止洗盘子,直到架上有新的空间可用。

在这个例子中哪里看出了条件变量呢?
这个条件变量就是盘子架是否满了,当盘子架满了,那么生产者等待盘子架有空余的空间时才开始工作,当盘子架空了,消费者等待其有碗了才开始工作,这就可以很好地理解条件变量了:条件变量就是在某些条件下使线程进行休眠或者唤醒的一种工作机制。

1.2、Condition接口

Condition接口是Java并发编程中的环境变量,它位于java.util.concurrent.locks包下,常与显式锁一起使用,使用Lock.newCondition()获得Condition对象。

public interface Condition {
	void await() throws InterruptedException;
	void awaitUninterruptibly();
	 long awaitNanos(long nanosTimeout) throws InterruptedException;
	 boolean await(long time, TimeUnit unit) throws InterruptedException;
	 void signal();
	 void signalAll();  
}
1.3、Condition接口方法

以上是Condition接口定义的方法,await对应于Object.wait,signal对应于Object.notify,signalAll对应于Object.notifyAll。特别说明的是Condition的接口改变名称就是为了避免与Object中的wait/notify/notifyAll的语义和使用上混淆,因为Condition同样有wait/notify/notifyAll方法。

  • await()方法:
    造成当前线程在接到信号或被中断之前一直处于等待状态。
  • signal()方法:
    唤醒特定的等待线程。
  • signalAll()方法:
    唤醒所有的等待线程。

二、实现一个生产者-消费者中的条件队列

2.1、条件变量的一般使用模式
 lock.lock();
	 try{
		while(条件判断){
			acondition.await();
		}

			//dosomething...
			bconditon.signal();
 }finally{
	lock.unlock();
}
2.2、使用条件变量实现一个生产者-消费者模式的队列
  • MyBlockingQueue
package cocurrency.product_and_consume_model;

import org.junit.Test;

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

/**
 * @Auther: ARong
 * @Date: 2019/11/30 2:06 下午
 * @Description: 使用可重入锁和条件变量来实现生产消费者模型队列
 */
public class MyBlockingQueue {
    private Lock lock = new ReentrantLock();
    private Condition isEmpty = lock.newCondition();
    private Condition isFull = lock.newCondition();
    // 存放数据的队列
    private LinkedList<Integer> queue = new LinkedList<>();
    // 最大长度
    private volatile int maxSize;

    public MyBlockingQueue(){
        this.maxSize = 1 << 4;
    }

    public MyBlockingQueue(int maxSize) {
        this.maxSize = maxSize;
    }

    /*
     * @Author ARong
     * @Description 获取数据
     * @Date 2019/11/30 2:17 下午
     * @Param []
     * @return int
     **/
    public int poll() {
        Integer value = -1;
        lock.lock();//执行到该处的线程获得锁
        try {
            while (queue.size() == 0){
                //队列为空,让线程在 isEmpty 这个条件变量中等待
                System.out.println(Thread.currentThread().getName()+"[队列为空,poll方法阻塞]");
                isEmpty.await();
            }
            value = queue.poll();
            System.out.println(Thread.currentThread().getName()+"[删除元素],当前空余容量为:"+(maxSize - queue.size()));
            //唤醒在 isFull 条件变量下等待的线程
            isFull.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//释放锁
        }
        return value;
    }

    /*
     * @Author ARong
     * @Description 放入数据
     * @Date 2019/11/30 2:17 下午
     * @Param [data]
     * @return void
     **/
    public void offer(int data) {
        // 获取锁
        lock.lock();
        try {
            while (queue.size() == maxSize) {
                // 队列已满,让该线程在 isFull条件中 等待
                System.out.println(Thread.currentThread().getName()+"[队列已满,offer方法阻塞]");
                isFull.await();
            }
            // 添加数据
            queue.offer(data);
            System.out.println(Thread.currentThread().getName() + "[添加数据],当前空余容量为:"+(maxSize - queue.size()));
            // 唤醒在isEmpty条件等待的线程
            isEmpty.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}
  • TestBlockingQueue
package cocurrency.product_and_consume_model;

import cocurrency.CountDownLatchDemo;
import org.junit.Test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: ARong
 * @Date: 2019/11/30 4:46 下午
 * @Description:
 */
public class TestBlockingQueue {


    @Test
    public void test() throws InterruptedException {
        // 线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0l, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
        // 创建一个只能容纳5个元素的阻塞队列
        MyBlockingQueue blockingQueue = new MyBlockingQueue(5);
        // 闭锁,等待两个线程完成任务才终止主线程
        CountDownLatch countDownLatch = new CountDownLatch(2);
        // 创建调用者去offer和poll
        threadPoolExecutor.submit(() -> {
            // 快速添加元素
            for (int i = 0; i < 10; i++) {
                blockingQueue.offer(i);
            }
            countDownLatch.countDown();
        });

        threadPoolExecutor.submit(() -> {
            // 缓慢获取数据
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(200l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                blockingQueue.poll();
            }
            countDownLatch.countDown();
        });
        // main线程等待
        countDownLatch.await();
        System.out.println("[任务执行完毕]");
    }
}

执行结果:

pool-1-thread-1[添加数据],当前空余容量为:4
pool-1-thread-1[添加数据],当前空余容量为:3
pool-1-thread-1[添加数据],当前空余容量为:2
pool-1-thread-1[添加数据],当前空余容量为:1
pool-1-thread-1[添加数据],当前空余容量为:0
pool-1-thread-1[队列已满,offer方法阻塞]
pool-1-thread-2[删除元素],当前空余容量为:1
pool-1-thread-1[添加数据],当前空余容量为:0
pool-1-thread-1[队列已满,offer方法阻塞]
pool-1-thread-2[删除元素],当前空余容量为:1
pool-1-thread-1[添加数据],当前空余容量为:0
pool-1-thread-1[队列已满,offer方法阻塞]
pool-1-thread-2[删除元素],当前空余容量为:1
pool-1-thread-1[添加数据],当前空余容量为:0
pool-1-thread-1[队列已满,offer方法阻塞]
pool-1-thread-2[删除元素],当前空余容量为:1
pool-1-thread-1[添加数据],当前空余容量为:0
pool-1-thread-1[队列已满,offer方法阻塞]
pool-1-thread-2[删除元素],当前空余容量为:1
pool-1-thread-1[添加数据],当前空余容量为:0
pool-1-thread-2[删除元素],当前空余容量为:1
pool-1-thread-2[删除元素],当前空余容量为:2
pool-1-thread-2[删除元素],当前空余容量为:3
pool-1-thread-2[删除元素],当前空余容量为:4
pool-1-thread-2[删除元素],当前空余容量为:5
[任务执行完毕]
【华为OD机试真题 2025C卷】895、贪心歌手 | 机试真题+思路参考+代码解析 (C++、Java、Py、C语言、JS) 【华为OD机试真题 2025C卷】895、贪心歌手 | 机试真题+思路参考+代码解析 (C++、Java、Py、C语言、JS) 华为od机试,华为od机试真题,华为od机试E卷,华为odE卷,华为od机试真题E卷,华为OD,华为OD机试,华为OD机试真题,华为od机考,华为od机试D卷,华为od机试真题D卷,华为od手撕代码,华为od,华为od机试C卷,华为od e卷,华为2025e卷,od机试,2025E卷,华为OD机试E卷,什么是华为od,华为od介绍,全面了解华为od,2025华为od,如何备战华 阅读详情

相关推荐

驱动兼容_FANUC 驱动器兼容问题探讨

近10年来,FANUC伺服驱动器随着系统的升级而升级,不同的系统,可以兼容很多种类的驱动器,作者做了一些常见经验总结如下表:电源模块伺服放大器主轴放大器bi一体18i/OIB/OIC6110-Hxxx6114-Hxxx6111-Hxxx6134-Hxxx31iA/OID6140-Hxxx6117-Hxxx6141-Hxxx6164-Hxxx31IB/35IB/OIF/OI MATE D(5包)62...

weixin_34420941的博客 1753

使用ReentrantLock和Condition实现生产者消费者

package mashibing; /** * 生产者消费者 */ import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java...

weiboKDhuo的博客 288

基于openEuler配置LAMP,部署WordPress

openEuler 基于OpenEular操作系统部署WordPress,必须包含以下内容: OpenEular的安装与基本配置。 基于SSH远程管理方式开展操作。 部署LAMP环境。 部署WordPress软件。 提供个人网站服务,并通过浏览器测试应用。 简要讲授相关知识与原理。 cd /etc/yum.repos.d 欧拉系统安装后,没有配置yum源,我们需要重新配置。 [root@localhost yum.repos.d]# ls -l 总用量 4 -rw-r--r--

Axiayouqiaomu的博客 6081

JAVA(五)ReentrantLock(重入锁)+Condition 生产者消费者模型

重点:ReentrantLock+Condition,await、signal方法 VS synchronized+Object的wait、notify 1. ReentrantLock对比snchronized ReentrantLock支持公平锁和非公平锁、可中断。 // 非公平锁(默认) final ReentrantLock lock = new ReentrantLock();...

muer_123的博客 545

ollama环境变量配置:修改ollama的数据路径(安装路径)

ollama安装模型,如果很大,会占用C盘空间,影响系统运行速度,通过设置环境变量,来改变ollama放目录的地址。

正经教主是个热衷于正经学习分享的教主。 1万+

界面上下固定_基于ANSYS的胶粘结构界面开裂有限元计算

引言:目前工程上主要有螺栓,过盈,焊接和胶粘四种常用的连接方式,本文基于ANSYS Workbench环境,引入内聚力模型来实现对胶粘结构界面开裂失效的计算,文中的界面失效参数为随意给定,不是工程实际测试数据,仅起到演示作用!1.问题的描述如图所示给出了胶粘结构的三维模型,该胶粘结构由上下钢板组成,中间通过胶粘接到一起。(胶不需要建模,其影响主要通过内聚力模型实现)上下板的材料为结构钢,...

weixin_29473667的博客 1万+

Java多线程问题--ReentrantLock和Condition实现多生产者/多消费者模式以及唤醒特定线程

本文内容部分引自《Java多线程编程核心技术》,感谢作者!!! 代码地址:https://github.com/xianzhixianzhixian/thread.git ReentrantLock和Condition基本用法 1、ReentrantLock类中的lock()和unlock()方法要成对使用,lock()方法使当前线程获得对象锁,unlock()方法使当前线程释放对象锁。...

樊先知樊先知的博客 725

从一只龙虾到一支团队:OpenClaw 单 Bot 多 Agent 配置实践

单 Bot 多 Agent只有一个对外的聊天入口(飞书 Bot)内部有多个独立的 AI Agent,各司其职主 Agent(总指挥)负责接收任务并分配给子 Agent子 Agent 完成任务后将结果返回给主 Agent角色Agent ID职责使用场景🎯 总指挥main-agent统筹协调,任务分发接收所有用户请求✍️ 笔杆子内容创作公众号、博客、小红书🔬 参谋canmou深度研究竞品分析、市场调研📋 运营官operations运营管理邮件、日历、社交媒体。

Maynor的博客 1084

ReentrantLock 配合Condition实现一个简单的生产者-消费者阻塞队列

ReentrantLock配合COndition实现一个简单的生产-消费阻塞队列

sky_sfsrew的博客 448

第3章: MMU Interval Notifier 区间通知器的核心原理⭐

摘要:区间级内存通知机制分析 本文分析了Linux内核中区间级内存通知机制(mmu_interval_notifier)的设计原理与实现。该机制针对传统全局通知(mmu_notifier)存在的性能开销大、无法精确控制等问题,通过引入区间树(Interval Tree)实现精确匹配,仅当内存变化与注册区间重叠时才触发回调。核心优势包括:1)O(log n + k)的高效查找性能;2)支持独立管理各内存区间;3)实现高并发处理不同区间的失效事件。

图形/计算GPU的驱动与虚拟化技术分享园地 2026

【推荐100个unity插件】unity易于使用模块化设计的天空、体积云和天气系统——Enviro 3

Enviro 3是一款功能强大的Unity天空与天气系统插件,提供动态天空、体积云、天气模拟等丰富功能。安装时需将预制体拖入场景并配置主相机,URP支持需激活深度纹理和添加渲染特性。模块化设计允许自由添加/移除时间、光照、雾效等模块,并通过API控制参数。推荐配合色调映射(如ACES模式)获得最佳视觉效果。插件支持季节、温湿度模拟及闪电风暴等特效,适用于需要逼真环境渲染的Unity项目。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2094

ReentrantLock和Condition 实现生产者 运输者 消费者

生产者、运输者、消费者 三个线程协作 使用公平锁实现, Condition 条件限制。 /** * 三个线程,一个生产商 A 一个中间商B 一个消费者C * 生产商 每次生产1个商品 * 中间商每次运送 2个商品 ,消费者每次消费3个商品 * 如果梳理不满足 则不运送 不消费 */ public class ThreePersionLockCondition { private final static ReentrantLock lock = new ReentrantLock(t

keep_learn的专栏 212

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

jdk11源码--ReentrantLock之Condition源码分析

文章目录概述创建Conditionawit 概述 在jdk11源码-ReentrantLock源码一文中分析了ReentrantLock源码。里面有讲述在多个线程加入队列时的AQS内部状态: 创建Condition 一个ReentrantLock可以创建多个Condition Condition condition = lock.newCondition(); 实际是创建一个ConditionO...

刘本龙的专栏 1351

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

java 生产者与消费者的几种实现方式

java 生产者与消费者的几种实现方式 1 synchronized的方式 public class MyClass { public static void main(String[] args){ final DataAssemble dataAssemble = new DataAssemble(); for(int i = 0; i < 20; i ++){ Thread thread = new Thread(new Run

chao18867105705的博客 242

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

JAVA并发编程--4.2理解Condition

理解Condition

拽着尾巴的鱼儿的博客 794

Java中ReentrantLock和Condition的实现原理

摘要:本文分析了Java中使用ReentrantLock和Condition实现的生产者-消费者模型,相比wait/notify具有精准唤醒优势。通过Condition将线程按条件分类,减少了无效唤醒和锁竞争。重点解析了await()方法流程:检查中断→加入条件队列→释放锁→判断是否被signal唤醒→重新获取锁。内部通过Node维护条件队列,配合unlinkCancelledWaiters清理无效节点,确保线程安全。该机制通过分离条件队列和同步队列,实现了高效的线程间通信。

qq_72722986的博客 376

java ReentrantLock结合条件队列 实现生产者-消费者模式 以及ReentratLock和Synchronized对比...

1 package reentrantlock; 2 3 import java.util.ArrayList; 4 5 public class ProviderAndConsumerTest { 6 7 static ProviderAndConsumer providerAndConsumer = new ProviderAndCon...

daxianqin6752的博客 369

利用J.U.C中的lock和condition实现生产者消费者模式

1 package lockTest; 2 3 import java.util.LinkedHashMap; 4 import java.util.LinkedList; 5 import java.util.concurrent.locks.Condition; 6 import java.util.concurrent.locks.Lock;...

weixin_30535043的博客 267

java多线程之Condition的使用

目录 一、Condition的概念 二、Condition的实现分析 (1)等待 (2)通知 三、Condition应用 (1)简单demo (2)可阻塞队列的应用案例 (3)多线程轮流执行 一、Condition的概念 回忆 synchronized 关键字,它配合 Object 的 wait()、notify() 系列方法可以实现等待/通知模式。对于 Lock,通过...

心猿意碼 1万+

Java阻塞队列的简单实现

来源: https://blog.biezhi.me/2019/01/simple-blocking-queue.html Java 并发常用的组件中有一种队列叫阻塞队列(BlockingQueue),当队列为空时,获取元素的线程会阻塞等待直到队列有数据;当队列满时,想要存储元素的线程会阻塞等待直到队列有空间。我们经常会用这种数据结构可以实现生产者、消费者模型。 本文会通过两种方式来实现简单的有界...

SpringForAll的博客 1272

ReentrantLock与Condition

文章目录1.ReentrantLock函数列表Condition 函数列表2.Condition的作用3.使用 参考博文1 1.ReentrantLock函数列表 // 创建一个 ReentrantLock ,默认是“非公平锁”。 ReentrantLock() // 创建策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。 ReentrantLock(boolean fair) // 查询当前线程保持此锁的次数。 int getHoldCou

weixin_42030357的博客 381

图解java.util.concurrent并发包源码系列——Condition条件等待队列深入详解

可以看到Condition接口除了普通的等待和唤醒方法,还提供了不响应中断(默认响应中断)和带超时机制的等待方法。可以看到ConditionObject内部带了头指针firstWaiter和尾指针lastWaiter。通过ReentrantLock的newCondition方法可以获取到ConditionObject对象。

weixin_43889578的博客 795

Java并发编程实战:生产者-消费者模型与BlockingQueue应用详解

在并发编程中,线程安全与资源共享是核心挑战。生产者-消费者模型通过解耦数据生成与处理过程,利用阻塞队列实现线程间高效协作。其技术价值在于平衡系统负载、提升吞吐量,并避免资源竞争导致的数据不一致问题。该模型广泛应用于任务调度、消息处理、数据缓冲等场景,是构建高并发系统的基石。本文以Java的BlockingQueue为例,结合分布式任务调度中的角色动态转换问题,深入解析如何通过线程安全队列实现优雅的并发控制。文章涵盖线程安全、阻塞操作、优雅关闭等关键机制,并提供完整代码示例与工程实践建议,帮助开发者掌握并发编

weixin_33861800的博客 509

经典问题之生产者-消费者问题——Lock实现

经典问题之生产者-消费者问题——Lock实现。 package com.lzq.newInterview; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;

泥土的博客 733

简易版消息队列的实现

简易版消息队列的实现 import java.util.Random; import java.util.Vector; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.IntStream; public class Factory {

JavaAlliance 1634

49、Java 生产者/消费者模式亲手实测采坑

生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色和一个内存缓冲区。 一个或者多个生产者 产生商品(数据) 并将商品存放到仓库中(内存缓冲区); 一个或者多个消费者 消费商品(数据)并将商品从仓库中取出(内存缓冲区); 仓库有存取功能,并且当仓库存满数据时,只取不存;当仓库没有商品时,只存不取。 生产者持续生产,直到缓冲区满,阻塞;缓冲区不满后,继续生产 消费者持续消费,...

FDoubleman的博客 425
上一篇: Java并发编程(10)-显式锁和读写锁
下一篇: Golang(1)-简介及特性
BoringRong
BoringRong 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 2万+粉丝 386原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值