博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java技术——ReentrantLock的Condition的作用以及使用
阅读量:4046 次
发布时间:2019-05-24

本文共 2443 字,大约阅读时间需要 8 分钟。

 

0. 前言  

之前知道ReentrantLock类有一个newCondition(),用于获取Lock上的一个条件,还可以多次newCondition()获得多个条件Condition可用于线程间通信。是对比ReentrantLockSynchronized关键字的区别时学习到的。但是有次面试被问到有没有用到过ReentrantLockCondition,瞬间懵逼了。所以搜集了些资料,通过一个小例子浅析一下ReentrantLockCondition。总结出的结果就是:

1通过Condition能够更加精细的控制多线程的休眠与唤醒

2对于一个锁,我们可以为多个线程间建立不同的Condition

 

1.  使用Condition实现一个ArrayBlockingQueue

我们将实现的MyArrayBlockingQueue类需要包括以下功能:

1)如果一个线程调用该类的take()获取元素时,若集合为空则使调用线程阻塞。直到有其他线程为集合加入新元素。

2)如果一个线程调用该类的put()添加新元素时,若集合满了则使调用线程阻塞。直到有其他线程从集合充take出数据。

 

1.1  内部成员以及构造方法

从下面源码中可以看出,我们使用了泛型,并且默认使用长度为10的数组来维护数据集合。定义了一个锁,并且根据锁的lock.newCondition()创建了两个条件,分别对应集合满和集合空两个条件

//维护的数据private final T[] datas;//数据的个数private int count;//插入取出的索引private int put_index;private int take_index;	//锁private final Lock lock = new ReentrantLock();//定义两个条件,分别为“集合满”和“集合空”private Condition full = lock.newCondition();private Condition empty = lock.newCondition();	//提供MyArrayBlockingQueue的构造方法,初始化T[]数据public MyArrayBlockingQueue() {    this(10);}	public MyArrayBlockingQueue(int maxSize) {    this.datas = (T[]) new Object[maxSize];}

 

1.2  put/get方法

public void put(T data){    lock.lock();    try {	if(count == datas.length){		//此时集合已经满了		System.out.println("集合已满,请等待...");		//使调用线程挂起		full.await();	}	//不满则添加新元素	datas[put_index++] = data;	count++;				//此时唤醒等待取数据的线程	empty.signalAll();			        } catch (Exception e) {	e.printStackTrace();	}finally{		lock.unlock();		}}public T take(){	lock.lock();	try {	    if(count == 0){		//此时集合已经空了		System.out.println("集合已空,请等待...");		//使调用线程挂起		empty.await();	    }				    //不空则取出最后一个数据	    take_index = count - 1;	    T result = datas[take_index--];	    count--;				    //此时唤醒等待写数据的线程	    full.signalAll();				    return result;				 } catch (Exception e) {		e.printStackTrace();	}finally{		lock.unlock();	}	return null;}

put方法中,如果集合满了,就调用await()方法使对应的线程释放锁,并且使调用线程阻塞。直到其他线程调用了take()方法,并调用了full.signalAll()时,该请求线程会被精准唤醒,重新竞争到锁后,代码继续往下执行。

若集合不满,则添加新元素,并且通过empty.signalAll()精准唤醒等待取数据的线程。

可以看到在take方法中也是类似的逻辑。这样就灵活并且方便的使用Condition完成了一个简单的线程安全的阻塞队列,一些角标等细节没有处理,毕竟主角是Condition

 

2.  总结

Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个LockCondition必须用newCondition()方法。Condition的强大之处在于,对于一个锁,我们可以为多个线程间建立不同的Condition

 

如果采用Object类中的wait(), notify(), notifyAll()实现的话,当写入数据之后需要唤醒读线程时,不可能通过notify()notifyAll()明确的指定唤醒读线程,而只能通过notifyAll唤醒所有线程,但是notifyAll无法区分唤醒的线程是读线程,还是写线程。所以,通过Condition能够更加精细的控制多线程的休眠与唤醒

 

转载地址:http://rkzci.baihongyu.com/

你可能感兴趣的文章
ping在类unix下的实现
查看>>
python下操作数据库
查看>>
python下对数据库的操作(2) 图片的存取
查看>>
常用排序算法总结(一) 比较算法总结
查看>>
剖析 Linux hypervisor
查看>>
常用排序算法总结(二)
查看>>
基于redhat的发行版本的linux系统 下 扩展Swap分区
查看>>
Fedora下安装Fcitx
查看>>
SSH原理与运用
查看>>
libvirt LXC driver --pass-fds
查看>>
Python ::OS模块 提供的接口介绍
查看>>
Python调用shell命令的几种方法(在新进程中执行shell命令)
查看>>
shell快捷键大全
查看>>
linux 4.15.7内核fedora(Ubuntu)下编译安装
查看>>
GRV – 可视化git仓库工具
查看>>
docker 镜像构建实践pagekit CMS(docker hub/docker cloud)
查看>>
搭建Docker Registry(2) 代理+认证(doing)
查看>>
简单Gitlab服务器的搭建(Gitlab+GerRit+Jenkins)
查看>>
简单GerRit服务器的搭建(Gitlab+GerRit+Jenkins)
查看>>
简单Jenkins服务器的搭建(Gitlab+GerRit+Jenkins)
查看>>