0318 guava并发工具
并发是一个难题,但是可以通过使用强力简单的抽象来显著的简化,为了简化问题,guava扩展了Future接口,即 ListenableFuture (可以监听的Future)。
我强烈建议你在你的所有代码里使用ListenableFuture去替代Future,原因如下:
- 很多的Futures 类的方法需要它。(Futures工具类使用)
- 它比后来改造为ListenableFutrue更简单。(早点使用比重构更简单)
- 工具方法的提供者不需要提供Future和ListenableFuture方法的变体。(不需要兼容两套)
接口
一个传统的Futrue代表一个异步计算的结果:一个可能完成也可能没有完成输出结果的计算。
一个Future可以用在进度计算,或者说是 一个提供给我们结果的服务的承诺。
一个ListenableFuture允许注册当你在计算完成的时候的回调,或者计算已经完成了。
这个简单的增强让高效支持多种操作成为可能。而Future接口并不能支持。
ListenbleFuture中添加的基本操作是
addListener(Runnable , Executor ),
它指出了当未来计算完成时,指定的Runnable会在指定的Executor中运行。
增加回调
很多用户喜欢使用 Futures.addCallback(ListenableFuture,FutureCallback,Executor)方法。
FutureCallback实现了下面两个方法:
- onSuccess(v) 当未来成功执行的动作,基于计算结果
- onFailure(Throwable) 当未来失败执行的动作,基于失败
创建
相较于jdk提供的 ExecutorService.submit(Callable)方法来初始化一个异步计算。它返回一个常规的Future,
guava提供了ListeningExecutorService接口,它返回ListenableFuture。
把ExecutorService转换为ListenableExecutorService
使用:MoreExecutors.listeningDecorator(ExecutorService)
基础用法如下:
/**
* 说明:使用例子代码
* @author carter
* 创建时间: 2020年03月19日 9:54 上午
**/
@Slf4j
public class ListenableFutureUtils {
public static void main(String[] args) {
ListeningExecutorService service = MoreExecutors.listeningDecorator(
Executors.newFixedThreadPool(10));
final ListenableFuture<AResult> listenableFuture = service.submit(() -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new AResult(30, "male", 1);
});
Futures.addCallback(listenableFuture,
new FutureCallback<AResult>() {
@Override
public void onSuccess(AResult aResult) {
log.info("计算成功,{}",aResult);
}
@Override
public void onFailure(Throwable throwable) {
log.error("计算错误",throwable);
}
},service);
}
@Data
@AllArgsConstructor
public static class AResult{
private Integer age;
private String sex;
private Integer id;
}
}
相对的,如果你想从基于FutureTask的API转换过来,
Guava提供了
ListenableFutureTask.create(Callable)
和
ListenableFutureTask.create(Runnable)
不同于jdk,ListenableFutureTask并不是直接扩展的。
如果你喜欢抽象的设置future的值,而不是实现一个方法然后计算值,可以考虑使用AbstractFuture或使用SettableFuture ;
如果你必须转换Future为ListenableFuture,你别无选择,必须使用 JdkFutureAdapters.listenInPoolThread(Future)来转换Future为ListenableFuture
任何时候只要可能,推荐你修改源码让它返回一个 ListenableFuture
应用
使用ListenablFuture最重要的原因是可以使用链式异步操作。
代码如下:
package com.xxx.demo;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 说明:异步操作链
* @author carter
* 创建时间: 2020年03月19日 10:11 上午
**/
public class ApplicationUtils {
public static void main(String[] args) {
Query query = new Query(30);
ListenableFuture<RowKey> rowKeyFuture = lookUp(query);
AsyncFunction<RowKey, QueryResult> queryFun = rowKey -> readData(rowKey);
final ListenableFuture<QueryResult> queryResultListenableFuture =
Futures.transformAsync(rowKeyFuture, queryFun);
}
private static ListenableFuture<QueryResult> readData(RowKey rowKey) {
return null;
}
private static ListenableFuture<RowKey> lookUp(Query query) {
return null;
}
@Data
@AllArgsConstructor
public static class RowKey {
private String id;
}
@Data
@AllArgsConstructor
public static class Query {
private Integer age;
}
@Data
@AllArgsConstructor
public static class QueryResult {
private String id;
private String age;
}
}
很多其他高效支持的操作ListenableFuture提供,而Future不提供。
不同的操作可以被不同的线程池执行,一个简单的ListenableFuture可以有多个操作去等待。
只要一个操作开始,其他多个操作应该开始,fan-out, 千帆竞发。
ListenableFuture可以实现这样的操作:它触发了所有请求的回调。
通过少量的工作,我们可以 fan-in.
触发一个ListenableFuture 来获得计算结果,当其他的Future结束的时候。
Futures.allAsList是一个例子。
方法介绍:
方法 | 描述 |
---|---|
transformAsync(ListenableFuture , AsyncFunction , Executor) | 返回一个新的ListenableFuture,它的结果是执行异步函数的返回,函数入参是ListenableFuture的返回结果; |
transform(ListenableFuture , Function , Executor) | 返回一个新的ListenableFuture,它的结果是执行函数的返回,函数入参是ListenableFuture的返回结果; |
allAsList(Iterable) | 返回一个ListenableFuture,它的结果是一个list,包含每一个列表中的ListenableFuture的执行结果,任何一个ListenableFuture执行失败或者取消,最后的返回结果取消 |
successfullAsList(Iterable) | 返回一个ListenableFuture,它的结果是一个list,包含每一个列表中的ListenableFuture的执行结果,成功的是结果,失败或者取消的值使用null替代 |
AsyncFunction<A,B> 提供了一个方法 , ListenableFuture apply(A inpunt),它可以用来异步的转换值。
代码如下:
package com.xxx.demo;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* 说明:成功执行结果汇集
* @author carter
* 创建时间: 2020年03月19日 10:34 上午
**/
@Slf4j
public class Test3 {
public static void main(String[] args) {
List<ListenableFuture<QueryResult>> querys = Lists.newLinkedList();
final ListenableFuture<List<QueryResult>> successfulAsList =
Futures.successfulAsList(querys);
Futures.addCallback(successfulAsList, new FutureCallback<List<QueryResult>>() {
@Override
public void onSuccess(List<QueryResult> queryResults) {
log.info("执行结果列表:{}",queryResults);
}
@Override
public void onFailure(Throwable throwable) {
log.error("执行失败",throwable);
}
});
}
@Data
@AllArgsConstructor
public static class QueryResult{
private Integer age;
}
}
嵌套的Future
你的代码调用一个通用接口并返回一个Future,很可能最终返回一个嵌套的Future.
package com.xxx.demo;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
/**
* 说明:嵌套的ListenableFuture
* @author carter
* 创建时间: 2020年03月19日 10:43 上午
**/
public class Test4 {
public static void main(String[] args) {
final ListeningExecutorService executorService = MoreExecutors
.listeningDecorator(Executors.newFixedThreadPool(2));
final ListeningExecutorService otherExecutorService = MoreExecutors
.listeningDecorator(Executors.newFixedThreadPool(2));
Callable<Foo> otherCallback = ()->new Foo("aaa");
final ListenableFuture<ListenableFuture<Foo>> submit =
executorService.submit(() -> otherExecutorService.submit(otherCallback));
}
@Data
@AllArgsConstructor
public static class Foo{
private String name;
}
}
例子最后返回的是: ListenableFuture<ListenableFuture> ,
这个代码不对,因为当外层的Future 取消的时候,无法传播到内层的Future,
这也是一个 使用get()检查别的Future或者Listnener的常规的错误,
但是,除非特别关注 否则 otherCallback抛出的异常会被压制。
为了避免这种情况,所有的guava的Future处理方法(有些从jdk来),有 *Async版本来安全的解开这个嵌套。
比如:transform,transformAsyn, submit, submitAsync方法。
深入研究
原创不易,转载请注明出处。
0318 guava并发工具的更多相关文章
- 008-guava 并发工具ListenableFuture、Service
一.ListenableFuture工具使用 guava 并发工具:ListenableFuture jdk8 提供了:CompletableFuture 实现,推荐使用 jdk 8 Completa ...
- Guava 开源工具的简单介绍
Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libra ...
- Guava并发:ListenableFuture与RateLimiter示例
ListenableFuture顾名思义就是可以监听的Future,它是对java原生Future的扩展增强 RateLimiter类似于JDK的信号量Semphore,他用来限制对资源并发访问的线程 ...
- 并发工具类:CountDownLatch、CyclicBarrier、Semaphore
在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch.CyclicBarrier.Semaphore. 一.CountDownLatch ...
- JavaEE Tutorials (27) - Java EE的并发工具
27.1并发基础427 27.1.1线程和进程42827.2并发工具的主要组件42827.3并发和事务42927.4并发和安全43027.5jobs并发示例430 27.5.1运行jobs示例4302 ...
- JUC学习笔记--JUC中并发工具类
JUC中并发工具类 CountDownLatch CountDownLatch是我目前使用比较多的类,CountDownLatch初始化时会给定一个计数,然后每次调用countDown() 计数减1, ...
- java 并发工具类CountDownLatch & CyclicBarrier
一起在java1.5被引入的并发工具类还有CountDownLatch.CyclicBarrier.Semaphore.ConcurrentHashMap和BlockingQueue,它们都存在于ja ...
- Java并发工具类 - CountDownLatch
Java并发工具类 - CountDownLatch 1.简介 CountDownLatch是Java1.5之后引入的Java并发工具类,放在java.util.concurrent包下面 http: ...
- java中的并发工具类
在jdk的并发包里提供了几个非常有用的并发工具类.CountDownLatdch.CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线 ...
随机推荐
- 使用记事本编写html代码并运行
在使用记事本编写html代码,运行时需要将其.txt后缀改为.html双击运行即可. 有时电脑会默认的隐藏其后缀,这时需要修改一下. win7系统修改方法: 双击 我的电脑: 选择 组织: 选择 ...
- C++如何保留2位小数输出
cout<<setiosflags(ios::);//需要头文件#include <iomanip> 然后再输出实数类型变量即可以保留2位小数输出了,当然你要保留三位小数,se ...
- 解密JDK8 枚举
写一个枚举类 1 2 3 4 5 6 public enum Season { SPRING, SUMMER, AUTUMN, WINTER } 然后我们使用javac编译上面的类,得到class文件 ...
- 什么是AWVS
什么是AWVS Acunetix Web Vulnerability Scanner(简称AWVS)是一款知名的网络漏洞扫描工具,它通过网络爬虫测试你的网站安全,检测流行安全漏洞,现已更新到10.(下 ...
- Simpo
Time: 2017-01-16 - Download Github: https://github.com/KeliCheng/Simpo一款快速发布文字和图片到社交网站的macOS菜单栏App,目 ...
- 为何银行愿为收购supercell做无权追索融资?
无追索权融资又称纯粹的项目融资,是指贷款人对项目主办人没有任何追索权的项目融资.简单来说,这是一种项目失败,也无法追尝的承诺,一般发生在石油.天然气.煤炭.铜.铝等矿产资源开发等相对较为保值的项目融资 ...
- 利用matplotlib进行数据可视化
matplotlib是python中的一个画图库,继承了matlib(从名字上也看得出来)的优点和语法,所以对于熟悉matlib的用户来说是十分友好的. pylab和pyplot 关于pylab和py ...
- 压力测试(九)-Jmeter压测课程总结和架构浅析
安装常见问题 1.问题 [root@iZwz95j86y235aroi85ht0Z bin]# ./jmeter-server Created remote object: UnicastServer ...
- Lambda表达式(JDK8)
在说Lambda表达式之前,先介绍一下函数式接口 函数式接口 就是只定义了一个抽象方法的接口,我们可以使用注解@Functionallnterface,来强约束这种接口为函数式接口.如Runnable ...
- SPA中前端路由基本原理与实现方式
SPA 前端路由原理与实现方式 通常 SPA 中前端路由有2中实现方式,本文会简单快速总结这两种方法及其实现: 修改 url 中 Hash 利用 H5 中的 history Hash 我们都知道 ur ...