生产者-消费者模式 系列 之二 ReentrantLock, Condition 篇

Java并行编程-lock中使用多条件condition(生产者消费者模式实例) Java 并发包下的提供Lock,Lock相对于Synchronized可以更好的解决线程同步问题,更加的灵活和高效,并且ReadWriteLock锁还能实现读、写的分离。但线程间仅仅互斥是不够的,还需要通信,本篇的内容是基于上篇之上,使用Lock如何处理线程通信。阻塞队列(BlockingQueue)就是使用condition的和lock实现的。可以查看:Java并发编程-阻塞队列(Blockin 阅读详情

     在生产者-消费者模式 系列 之一 Sychronized,Notify,Wait 篇中对Java多线程通讯,同步的典型知识点做了介绍. 这篇文章将主要集中在JDK 1.5中引入的 lock 机制和其对应的condition 条件变量.  将通过ReentrantLock 来实现系列一中同样功能的生产者-消费者模式.

     Lock :  Lock概念的出现,乍一看是把Java中一直使用的隐式对象锁(可参考系列一文章synchronized中锁类型的实验例子)以一种独立明确的方式进行了定义. 使得在多线程环境中对共享资源的访问控制,除synchronized之外,又多了一种选择. 但仔细琢磨你会发现, Lock还有很多synchronized无法比拟的优势.

  •  Lock 提供了tryLock方法,这样一旦锁被其他线程占用, 调用该方法不至于导致线程被阻塞. 而synchronized 会导致线程阻塞.
  •  Lock 有不同的实现. 比如我们即将用到的reentrantLock ,它实现的是一个排它锁; ReadWriteLock实现的是读写锁,适用于大量读而少量写的场景.
  •  通过调用Lock.newCondition() 方法, 可以获得绑定在该Lock上的条件变量. 从而对线程的阻塞和唤醒有更灵活的控制. 在这个例子中会有展现.

当然,事物都要两面性,Lock同样也不例外. 它的引入也会带来一些负作用. 比如

  • 必须用户显示地调用Lock 去获得锁,然后还要确保unlock被调用从而释放锁. 于是就有了下面比较繁琐的代码段. 感觉Java 在走C++的路,想想垃圾回收.
try{ 
lock.lock(); 
//do something
} finally{
lock.unlock();
}
  • Lock 同样也是对象,这样它本身也有notify 和 wait 方法. 看看如下代码是否很费解?  
private Lock lock = new ReentrantLock();
synchronized(lock){
  //do something
  lock.notify();
}


     ReentrantLock : Lock 接口的一个实现. 一个互斥锁. 基本上就是synchronized 的替代品. 但作为Lock接口的实现也继承了其优势. 在对阻塞进程进行唤醒时比object.notify 多了一些自主权.  Object.notify方法不能指定算法说哪个线程将被唤醒. 但 ReentrantLock 由于在构造函数中有 fairness 参数,当为true时,可以将等待时间最长的线程优先唤醒.

     Condition :  此概念的出现,将Object的Notify,wait方法进行了独立. 并且可以和Lock对象通过Lock.newCondition() 方法自由绑定. 控制更加灵活. 另外 Condition.await 和Condition.signal 方法都是原子操作.  Condition 概念理解起来不太容易. 尤其是看到JDK自带的Javadoc 关于BoundedBuffer的示例之后,里面的notFull 和notEmpty相当拗口. 但仔细琢磨你会发现它原来就是一个条件变量 相当于说 

if ( condition variable == true ) 
  signal; 
else
  await;
end;
 

所以, 在命名Condition 变量时, 要根据你的上下文,给它赋予一个有意义,好辩识的名字.

 

 生产者消费者场景假定

  •        同时只能有一个生产者进行生产.生产的同时,不能有消费者在消费.
  •        同时只有一个消费者在消费.消费的同时,不能有生产者生产.
  •        生产者最多能生产10个产品. 如果当前产品数超过10个,生产者将等待直到产品数小于10才开始生产.
  •        当前如果没有可消费产品时,消费者将等待直到有产品可消费为止.
import java.util.Stack;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockCPTest {



	public static void main(String[] args) {
		
		Repository s = new Repository();
		Maker f1 = new Maker(s,"P001");
		Maker f2 = new Maker(s,"P002");
		
		f1.start();
		f2.start();

		Taker c1 = new Taker(s,"C001");
		Taker c2 = new Taker(s,"C002");
		Taker c3 = new Taker(s,"C003");
		c1.start();
		c2.start();
		c3.start();
		
	}

}
class Repository{
	
	private  final static int MAX_ELEMENT = 10;
	
	private Stack<String> _store = new Stack<String>();
	
	private Lock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

    
	
	public  void add(String in) throws InterruptedException{
		
		lock.lock();
		
		try{
		    while (this._store.size() >= MAX_ELEMENT) {//这里一定是Loop 循环,因为被阻塞的生产者线程被唤醒后要继续执行,但之前必须判断产品库是否已满.
				System.out.println(Thread.currentThread().getName() + " is waiting on add.");
				notFull.await(); //如果参数中数量达到10个的最大值.生产者线程等待.
				System.out.println(Thread.currentThread().getName() + " is after waiting on add.");
			}
			this._store.push(in);
			System.out.println(Thread.currentThread().getName() + " is adding product "+ in+". Remaining size is "+ this._store.size());
			notEmpty.signal(); //唤醒那些因为产品库为零时等待的消费者进程.

		}finally{
			lock.unlock();
		}




	}
	
	public   String  get() throws InterruptedException{
		lock.lock();
		String rtn = "";
		
		try{
			
			while (this._store.isEmpty()) {//这里一定是Loop 循环,因为被阻塞的消费者线程被唤醒后要继续执行,但之前必须判断产品库是否为空.
				System.out.println(Thread.currentThread().getName() + " is waiting on get.");
				notEmpty.await();//如果产品库为空,消费者线程等待.直到产品库中有产品时被唤醒.
				System.out.println(Thread.currentThread().getName() + " is after waiting on get.");
				
			}
			
			rtn = this._store.pop();
			System.out.println(Thread.currentThread().getName() + " is getting product "+ rtn+". Remaining size is "+ this._store.size());
			notFull.signal();//唤醒因产品库为空可能导致等待的生产者线程.
			
		}finally{
			lock.unlock();
		}
	


		return rtn;
	}

}

class Maker implements Runnable {
	   
	private Repository _store;
	private String _name;
	private Thread _thread;
	public Maker(Repository s,String name) {
	super();
	this._store = s;
	this._name = name;
	this._thread = new Thread(this,name);
	}
	
    public void start(){
    	this._thread.start();
    }
    
	@Override
	public void run() {
		int i = 0;
		while(true){
			try {
				this._store.add(this._name + " Product "+ ++i);
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (Exception e){
				e.printStackTrace();
			}
		}

	}

}


class Taker implements Runnable {
	  
	private Repository _store;
	private Thread _thread;
	public Taker(Repository s,String name) {
		super();
		this._store =s;
		this._thread = new Thread(this,name);
	}
    public void start(){
    	this._thread.start();
    }
    
	@Override
	public void run() {
		while(true){
			try {
				this._store.get();
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

}



运行结果如下

 

 

P002 is adding product P002 Product 1. Remaining size is 1
P001 is adding product P001 Product 1. Remaining size is 2
C002 is getting product P001 Product 1. Remaining size is 1
C001 is getting product P002 Product 1. Remaining size is 0
C003 is waiting on get.
P001 is adding product P001 Product 2. Remaining size is 1
P002 is adding product P002 Product 2. Remaining size is 2
C003 is after waiting on get.
C003 is getting product P002 Product 2. Remaining size is 1
P002 is adding product P002 Product 3. Remaining size is 2
P001 is adding product P001 Product 3. Remaining size is 3
P001 is adding product P001 Product 4. Remaining size is 4
P002 is adding product P002 Product 4. Remaining size is 5
P002 is adding product P002 Product 5. Remaining size is 6
P001 is adding product P001 Product 5. Remaining size is 7
C001 is getting product P001 Product 5. Remaining size is 6
P002 is adding product P002 Product 6. Remaining size is 7
C002 is getting product P002 Product 6. Remaining size is 6
P001 is adding product P001 Product 6. Remaining size is 7
C003 is getting product P001 Product 6. Remaining size is 6
P002 is adding product P002 Product 7. Remaining size is 7
P001 is adding product P001 Product 7. Remaining size is 8
P002 is adding product P002 Product 8. Remaining size is 9
P001 is adding product P001 Product 8. Remaining size is 10
P002 is waiting on add.
P001 is waiting on add.
C001 is getting product P001 Product 8. Remaining size is 9
C002 is getting product P002 Product 8. Remaining size is 8
P002 is after waiting on add.


 

机械臂视觉抓取避坑指南:如何正确计算手眼标定矩阵(附Numpy代码) 本文详细解析了机械臂视觉抓取中手眼标定矩阵的计算方法,特别针对'眼在手上'配置下的常见错误提供了解决方案。通过Numpy代码示例和坐标转换原理,帮助开发者避免矩阵计算错误和坐标转换偏差,提升工业自动化系统的精度和效率。 阅读详情

相关推荐

TPAMI 2025新作LRFormer复现笔记:我的Windows 11 + RTX 4090配置全流程

本文详细记录了在Windows 11系统下使用RTX 4090显卡复现TPAMI 2025顶会论文LRTransformer的完整流程,包括硬件配置、系统优化、Python环境搭建及模型训练中的常见问题解决方案。特别针对Windows平台特有的路径管理、CUDA版本冲突和显存优化等关键技术细节提供了实用指南,帮助开发者高效完成环境搭建和模型训练。

weixin_29094301的博客 411

03多线程-用ReentrantLock实现生产者和消费者模式

ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重入性,synchronized通过获取自增,释放自减的方式实现重入。与此同时,ReentrantLock还支持公平锁和非公平锁两种方式。

rainyonelove的专栏 1283

BK3296蓝牙音频SoC深度评测:如何用这颗芯片打造超低功耗TWS耳机?

本文深度评测BK3296蓝牙音频SoC在TWS耳机中的应用,重点解析其22nm工艺带来的超低功耗优势(A2DP模式仅2.8mA)和3×3mm封装设计。通过芯片架构分析、双麦降噪调优及开发套件使用指南,为工程师提供打造高性能TWS耳机的完整解决方案,显著提升续航和音频质量。

rice5的博客 654

快手java开发面试经验大全

1.自我介绍 2.java集合、hashmap详细介绍、关键参数、线程安全的集合、队列和栈、用两个栈实现队列算法 3.jvm结构 4.多线程锁:synchronized、reentrantlock、countdownlatch、锁升级 5.mysql隔离级别 6.线程池有哪些、核心参数、线程池运行机制 7.threadlocal 8.写线程安全单例模式,volatile原理、 9.写单词拆分问题 10.反问 好像叫基础平台部门。java岗。 总体来说,快手的一二面会根据难度循序渐进,并且会出一两个由易到难的

begefefsef的博客 1万+

总结-NuerIPS2023-Direct Preference Optimization:Your Language Model is Secretly a Reward Model

对比PPO,DPO的泛化能力更强。

m0_60361484的博客 1606

【fNIRS可视化学习1】基于NIRS-SPM进行光极可视化并计算通道坐标

设头皮上的目标点为P₀(x₀, y₀, z₀),需要在大脑皮层点集S上找到最合适的投影点P'。

Rose9614的博客 2207

ReentrantLock实现生产者消费者模式

ReentrantLock 实现生产者消费者模式 实现生产者消费者模式:一对一交替打印 public class MyService { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); private boolean hasValue = false; public void set() { try {

weixin_42430947的博客 436

AMD Ryzen AI Max+ 395四机集群实战:开源工具链与大模型推理优化全解析

本文详细解析了基于AMD Ryzen AI Max+ 395处理器的四机集群搭建与优化实践,涵盖硬件配置、开源工具链评测及大模型推理优化技巧。通过深度测试,揭示了集群在AI推理中的性能瓶颈与解决方案,特别适合需要扩展显存的大模型实验场景。文章还提供了成本效益分析,帮助开发者做出更明智的选型决策。

weixin_29041767的博客 1515

ReentrantLock的介绍使用及“生产者消费者”模式实现

一、简介 ReentrantLock 即可重入锁的意思,是 java 控制线程同步的一种方式。和 synchronized 一样都是独占锁,只允许拿到锁的线程访问临界区。 synchronized 优点是使用简单,不需要用户手动操作,加锁解锁的过程都是隐式的。 ReentrantLock 则需要我们手动加锁和解锁(解锁操作通常放在 finally 代码块中保证执行),虽然使用更复杂但是其灵活性更好...

阿飞的博客 1676

【DRAM存储器五十一】LPDDR5介绍--DFE与Pre-Emphasis

简述LPDDR5的DFE和预加重应用设置

highman110的博客 703

智能体异常行为检测-基于行为序列的异常模式识别技术【附代码】

在多智能体系统(Multi-Agent Systems, MAS)中,智能体之间的行为协调与自主决策是系统稳定运行的关键。然而,当某个智能体因策略偏移、环境变化或潜在攻击而出现**异常行为(Abnormal Behavior)**时,整个系统可能陷入混乱或崩溃。

一键难忘的博客 1143

基于ReentrantLock实现简易生产者、消费者模型

实际开发中,我们可能会需要一个队列场景,往队列投入资源的为生产者,往队列中拿元素的为消费者,那么ReentrantLock能帮助我们实现这种生产者、消费者模型。 实现原理: 我们可以借助ReentrantLock定义两个信号量: notFull和notEmpty, notFull.await()阻塞生产者投放资源,同时提醒消费者可以拿资源,notFull.signal() 用来提醒生产者可以继续投放资源 ; notEmpty.await用来阻塞消费者拿资源并提醒生产者往队列...

Dream_it_possible!的博客 2640

【推荐100个unity插件之34】在unity中实现和Live2D虚拟人物的交互——Cubism SDK for Unity

Live2D是一款2D建模的软件,常用于游戏,动态壁纸,虚拟主播,游戏公司里面是有专门的ive2D岗位的。游戏里面还有一个经常使用的软件——spine,他做的也是2d的模型,但是他更适合做一些动作,Live2D更适合人物交互方面。

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

详解ReentrantLock之Condition原理

Condition Jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock。虽然在性能上ReentrantLock和synchr...

Daniel的博客 1万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

ReentrantLock详细讲解,什么是ReentrantLock锁,什么是Condition条件变量(二)

juc、ReentranLock

zhangfuping123456789的博客 8372

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

用Condition条件变量实现生产者消费者模式

import java.util.LinkedList; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ProducerConsumerPattern{ public static final int MAX_CAP = 20;

Just Do IT 2472

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

使用Condition之生产者消费者

使用Condition之生产者消费者

weixin_45428910的博客 862

Java多线程13:手写生产者-消费者模式

一、Object的wait、notify和notifyAll方法 1.1、wait方法 当一个线程调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起(进入waiting状态),直到发生下面几件事情之一才能返回: 其他线程调用了该共享对象的notify()或者notifyAll()方法; 其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回。 另外需要注意的是,如果调用wait()方法的线程没有事先获取该对象的监视器锁,则调用w...

何哥的博客 2811

使用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的博客 287

使用ReentrantLock实现生产者消费者模式

使用ReentrantLock实现生产者消费者模式 一. 生产者/消费者模式:一对一交替打印 新建MyService.java : package service;   importjava.util.concurrent.locks.Condition; importjava.util.concurrent.locks.ReentrantLock;   public class

ForeverLove的博客 871

ReentrantLock与Condition实现生产者消费者,模拟延时用LockSupport.parkNanos

使用ReentrantLock与Condition实现生产者消费者 主要有Producer,Consumer,Pool 生产池 在测试多线程程序时,最后【多建线程】才能明显看出并发时,是否数据异常。建议10到30个线程 创建的线程数少,模拟的延时太高,多线程可能会依次执行,不能判断是否线程安全 Pool生产池,封装也生产与消费的方法,注意里面的【while循环】判断Pool是否满或者空,负责会产出...

老专家的博客 2616

condition实现生产者和消费者

生产者 import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; public class ConditionProducer implements Runnable { private Lock lock; private Condition condition; p

Emine 1431

Java中的生产者/消费者模型

生产者-消费者模型(Producer-Consumer problem)是一个非常经典的多线程并发协作的模型。比如某个模块负责生产数据,而另一个模块负责处理数据。产生数据的模块就形象地被称为生产者;而处理数据的模块,则被称为消费者。生产者和消费者在同一段时间内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。

u010257931的博客 6812

最简单的生产者消费者模型(ReentrantLock锁及其Condition机制)

一个生产者 一个消费者 仓库大小为1 消费者类 public class Consumer implements Runnable{ List&amp;lt;Integer&amp;gt; cache = new ArrayList&amp;lt;&amp;gt;( ); ReentrantLock lock; Condition notFull; Condition notEmpty; ...

Fann 456

生产者消费者模式全方位解析:从原理到高并发实战

生产者消费者模式是一种经典的多线程协作方案,通过缓冲区解耦生产者和消费者。生产者将数据存入缓冲区,消费者从中取出处理,两者无需直接交互。该模式具有四大优势:解耦、并发支持、平衡速度差异、支持批量处理。实现核心在于线程安全的阻塞队列,使用synchronized和wait/notify机制确保同步。这种模式广泛应用于高并发系统,如消息队列、日志处理等场景。

百锦再的博客 951

生产者消费者的实现与思考

生产者消费者的Java实现生产者:负责生产消息,在缓冲区满后休眠; 消费者:负责消费消息,在缓冲区空后休眠;两者的休眠何时唤醒?1.生产者休眠,是因为缓冲区满,所以只要消费者进行了消费,那么缓冲区就会有新的空间,生产者就可以继续生产,故每次消费者消费以后都要试图唤醒生产者,无论生产者是否休眠。2.消费者休眠,是因为缓冲区空,所以只要生产者进行了生成,那么缓冲区就会有新的消息,消费者就可以继续生产,

潜心种树 5511
上一篇: 生产者-消费者模式 系列 之一 Sychronized,Notify,Wait 篇
下一篇: 分治算法题之数组元素求和
TechNerd
博客等级 码龄14年 5粉丝 26原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值