Guava系列之Concurrent-阿里云开发者社区

开发者社区> 开发与运维> 正文

Guava系列之Concurrent

简介: guava翻译系列

并发

Guava 已经在java的集合方面提供了非常多有用的方法,但是Guava真正的发光点在并发这一块。 在Java5 中引入了java.util.concurrent 包。 concurrent 在java中已经变得非常容易实现了。 Guava构建这些结构之上,并且提供许多非常有用的功能点在com.google.util.concurrent包中。

在这一章中我们将涉及到:

  • Monitor 类的功能有点类似 Mutex,保证在多线程先能够串行的访问我们定义的代码块, 更像我们的关键字synchronized, 但是比synchronized更加好用并且多出来一些额外的功能点。
  • ListenableFuture 类 和java 中的Listenable相同的功能,唯一不同的是我们可以注册一个回调接口,当ListenableFuture完成时会回调指定的接口
  • FutureCallback 类给我们处理成功或失败的接口
  • SettableFuture,AsyncFunction,FutureFallback 类是非常有用的工具类,我们可以用这些工具类转换objcts
  • Futures 类是 Future 接口的实现,里面包含很多有用的静态方法
  • RateLimiter 类控制线程access资源的频率,这个有点像信号量,不过信号量是从数量上控制,而RateLimiter是基于时间从频率上进行控制。

同步线程

在Java中提供了多线程运行的能力,我们在有些场景中需要控制在同一个时间内只能有一个线程运行某一部分代码,在java中提供了synchronized关键字来控制序列化访问。 但是使用synchronized关键字也有一些问题,首先 如果我们要调用thread的wait()方法,那必须记住我们要用while循环,因为可能会出现中断,假唤醒:

while(someCondition){
try {
wait();
} catch (InterruptedException e) {
//In this case we don't care, but we may want
//to propagate with Thread.interrupt()
}
}

如果说有多个条件导致线程进入wait()状态,Second, if we have more than one condition that can cause a thread to go into a wait
state, we must call notifyAll(), as we don't have the ability to notify threads for
specific conditions. Using notifyAll() instead of notify() is less desirable due to
the thrashing effect it has of waking up all the threads to compete for a lock when
only one will do so. java 5 中引入ReentrantLock(可重复锁)类,使用ReentrantLock可以创建不同的等待条件,这样我们就可以使用Condition.singal()方法来精确唤醒某一个单独的线程,Condition也有notifyAll()方法,这个方法和Object的notifyAll()有着相同的副作用。 虽然是可以唤醒某个单独的条件,我们依然要使用while条件循环.

while(list.isEmpty()){
Condition.await();
}

使用guava的Monitor类可以解决这个问题

Monitor

The Monitor class from Guava gives us a solution that allows multiple conditions
and completely eliminates the possibility of notifying all threads by switching from
an explicit notification system to an implicit one. Let's take a look at an example:

public class MonitorSample {
private List<String> list = new ArrayList<String>();
private static final int MAX_SIZE = 10;
private Monitor monitor = new Monitor();
private Monitor.Guard listBelowCapacity = new
Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return list.size() < MAX_SIZE;
}
};
public void addToList(String item) throws InterruptedException
{
monitor.enterWhen(listBelowCapacity);
try {
list.add(item);
} finally {
monitor.leave();
}
}
}

我们来看一下上面例子中比较有意思的部分,首先我们创建了一个Monitor类,接着使用刚创建的Monitor类创建Guard类,Guard类有一个抽象方法,isSatisfied,这个方法返回一个boolean值,这里我们当List的长度小于10个时,Guard返回true。最后调用addToList方法,当Guard条件返回true的时候,并且只有一个线程可以进入。线程可以进入Monitor并且将item里面加入到List中,这里值得注意的是,我们没有明确的的唤醒某一个线程,而是只要符合条件就可以进入到Monitor. 下面我们仔细研究一下Monitor类。

Monitor explained

当一个线程进入Monitor区域,就认为该线程持有Monitor实例,当调用leave()方法,该线程就不再持有Monitor实例,同一时间只有一个线程可以进入到Monitor区域。 这种机制和synchronized reentrantLock 一样,有而只有一个线程可以进入到block区中,同一个线程可以任意多次进入想到的Monitor 区域,但是没一次进入之后都要调用leave方法。

Monitor best practice

Monitor方法的使用场景中一般都要使用try/finnally 区域,以确保能够释放锁。

if (monitor.enterIf(guardCondition)) {
try {
doWork();
} finally {
monitor.leave();
}
}

对于不返回任何值的Monitor方法,依然要加上try/finally方法区

monitor.enterWhen(guardCondition);
try {
doWork();
} finally {
monitor.leave()
}

不同的 Monitor 方法

Monitor类有很多进入block的方法,下面有5种基本类型的方法

版权声明:本文首发在云栖社区,遵循云栖社区版权声明:本文内容由互联网用户自发贡献,版权归用户作者所有,云栖社区不为本文内容承担相关法律责任。云栖社区已升级为阿里云开发者社区。如果您发现本文中有涉嫌抄袭的内容,欢迎发送邮件至:developer2020@service.aliyun.com 进行举报,并提供相关证据,一经查实,阿里云开发者社区将协助删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章