一. ListenableFuture是用来增强Future的功能的。
我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果。如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,不断代码复杂,而且效率低下。
ListenableFuture,顾名思义,就是可以监听的Future。我们可以为ListenableFuture增加Listener监听器,当任务完成时,直接执行某个线程,或者我们可以直接为ListenableFuture设置回调函数,当任务完成时,自动执行该回调方法。
下面我们看看ListenableFuture的实际用法。
(1)怎样得到ListenableFuture对象?
我们知道ExecutorService.submit(Callable) 会返回Future对象,那么ListeningExecutorService.submit(Callable)会返回ListenableFuture对象。
ListeningExecutorService可以通过MoreExecutors.listeningDecorator(ExecutorService)来得到。
(2)增加Listenable功能
方法一:直接添加监听
ListenableFuture. addListener(Runnable listener, Executor executor)
可以为ListenableFuture增加一个监听,当线程计算完成时,自动执行一个Runnable线程。
方法二:添加回调方法
Futures.addCallback(ListenableFuture, new FutureCallback<T>() {
public void onSuccess(T t) {
}
public void onFailure(Throwable thrown) {
}
});
Futures的静态方法addCallback可以为ListenableFuture对象添加回调函数,回调里面可以定义计算成功时和失败时分别的操作。onSuccess里面可以把计算结果做为参数传入。onFailure里面可以把异常做为参数传入。
那我们一般应该怎么选择这两种方式呢?建议直接用第二种,因为这种方式可以把计算结果做为参数传入。其实,第二种的内部实现就是用的第一种方式,但是用起来会更加的简洁。
(3)ListenableFutureTask
我们知道,FutureTask实现了Future和Runnable接口。
FutureTask有2个构造方法:
FutureTask(Callable<V> callable)
创建一个 FutureTask,一旦运行就执行给定的 Callable。
FutureTask(Runnable runnable, V result)
创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果
Thread thread = new Thread(futureTask);
thread.start();
当futureTask被某个线程执行时,就会自动执行Callable或者Runnable,Callable的call方法返回计算结果,对应Runnable作为参数的版本,另一个参数V result将作为结果进行返回。
当计算完成时,futureTask.get()方法就会返回这个结果。
可以看到,这段代码很复杂,而且计算完成时缺乏回调机制。ListenableFutureTask正是为了解决这些问题。ListenableFutureTask同样有2个版本的方法。
ListenableFutureTask.create(Callable<V>)
ListenableFutureTask.create(Runnable, V)
很简单,这2个方法都返回ListenableFutureTask<V>对象,然后就可以用之前所说的方式直接添加回调函数或者回调接口。
(4)返回值固定,不需要计算时
有时候,我们的Future不需要实现一个方法来计算返回值,而只需要返回一个固定值来做为返回值,那么可以直接使用 SettableFuture,或者跟 SettableFuture 一样,自己来继承AbstractFuture<V>。
SettableFuture提供了2个方法,分别用来设置成功时的返回值,或者失败时抛出的异常。
booleanset(V value)
booleansetException(Throwable throwable)
另外还有静态方法create()会构造一个初始状态的SettableFuture对象。
示例代码:
SettableFuture<Integer> future = SettableFuture.create();
future.set(100);//自己根据业务逻辑,来设置返回值
// or future.setException(new Throwable()); //或者计算失败,抛出异常
Futures.addCallback(future, new FutureCallback<Integer>() {
@Override
public void onSuccess(Integer result) {
}
@Override
public void onFailure(Throwable t) {
}
});
(5)旧代码改造:将Future封装为ListenableFutureTask
对于老的使用Future对象的代码,如果希望使用ListenableFutureTask,可以考虑使用JdkFutureAdapters.listenInPoolThread(Future) 来将Future对象直接封装为ListenableFutureTask。
但是,guava规范强烈建议我们还是按照之前的方法来产生ListenableFutureTask对象,所以如果时间允许,最好代码重构更加彻底一点。
(6)ListenableFutureTask链条
有时候,我们得到一个计算结果后,会再调用另一个方法,再次进行计算,得到一个新的计算结果,这时候我们可以使用Futures.transform方法来将两次计算链起来。
public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input,
AsyncFunction<? super I, ? extends O> function,
Executor executor)
Input表示第一个异步计算任务,AsyncFunction表示得到计算结果后,怎样再次转换为另一个计算任务,Executor表示运行转换函数的线程。
这个理解起来确实费劲,还是看一个示例吧:
//get a random integer
class Task1 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("task1 begin");
Thread.sleep(2000);
System.out.println("task1 end");
return new Random().nextInt();
}
}
//add suffix to an integer
class Task2 implements Callable<String>{
private Integer i;
public Task2(Integer i){
this.i = i;
}
@Override
public String call() throws Exception {
System.out.println("task2 begin");
Thread.sleep(4000);
System.out.println("task2 end");
return i+"suffix";
}
}
@Test
public void testLisenableFutureChain() throws InterruptedException, ExecutionException{
ExecutorService normalService = Executors.newFixedThreadPool(100);
final ListeningExecutorService service = MoreExecutors.listeningDecorator(normalService);
ListenableFuture<Integer> future1 = service.submit(new Task1());
AsyncFunction<Integer,String> asyncFunction = new AsyncFunction<Integer,String>(){
@Override
public ListenableFuture<String> apply(Integer input) throws Exception {
return service.submit(new Task2(input));
}};
ListenableFuture<String> futures2 = Futures.transform(future1, asyncFunction);
System.out.println(futures2.get());
}
这个例子展示了两个任务是怎样依次执行的。第一个任务得到一个整数,第二个任务在整数的后面加上后缀。第一个任务完成后立刻执行第二个任务。
除了transform(ListenableFuture<A>, AsyncFunction<A, B>, /*Executor*/)方法,Futures中还有几个其他的方法,也是用于几个Listenable任务组合执行:
Futures.transform(ListenableFuture<A>, Function<A, B>, /*Executor*/) 转换的函数是同步函数,内部处理其实是把这个Function转化为立刻返回成功的AsyncFunction,然后调用参数为AsyncFunction的transform方法。
Futures.allAsList(Iterable<ListenableFuture<V>>) 将多个任务的计算结果拼成一个list对象。注意:只要有一个任务计算失败,整个任务就是失败的。
Futures.successfulAsList(Iterable<ListenableFuture<V>>) 同上,但是只把计算成果的结果返回,对于计算失败的任务直接抛弃。
(7)CheckedFuture
得到ListenableFuture对象后,如果执行失败,调用get方法会直接抛出系统异常,如InterruptedException,CancellationException,ExecutionException,我们可能需要手动捕获这些异常,然后转换为应用异常。
CheckedFuture继承自ListenableFuture,新增了checkedGet方法,这个方法会自动把InterruptedException,CancellationException,ExecutionException转化为指定的异常类型。
Futures.makeChecked(ListenableFuture<V>, Function<Exception, X>)可以把普通的ListenableFuture转换为CheckedFuture类型。Function用于定义这三种异常分别转换为何种异常。
二.Guava的线程支持除了ListenableFuture,还有个ServiceManager,如果你想编写一个类似Tomcat的服务器程序,这个可以派上用场。
Service代表一个服务,ServiceManager用来对这些服务进行管理和状态维护。
Service.State这个枚举类型定义了service常见的6种状态:FAILED,NEW,RUNNING,STARTING,STOPPING,TERMINATED。
一般的Server可以按下面的方式实现。
class Server {
public static void main(String[] args) {
Set<Service> services = ...;
ServiceManager manager = new ServiceManager(services);
manager.addListener(new Listener() {
public void stopped() {}
public void healthy() {
// Services have been initialized and are healthy, start accepting requests...
}
public void failure(Service service) {
// Something failed, at this point we could log it, notify a load balancer, or take
// some other action. For now we will just exit.
System.exit(1);
}
},
MoreExecutors.sameThreadExecutor());
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
// Give the services 5 seconds to stop to ensure that we are responsive to shutdown
// requests.
try {
manager.stopAsync().awaitStopped(5, TimeUnit.SECONDS);
} catch (TimeoutException timeout) {
// stopping timed out
}
}
});
manager.startAsync(); // start all the services asynchronously
}
}}
相关推荐
Guava学习笔记笔记的大部分中文翻译内容来源于以下译者: 沉义扬,罗立树,何一昕,武祖校对:方腾飞感谢给我们带来的这一系列好文章。引言Guava工程包含了若干被Google的Java项目广泛依赖的核心库,例如:集合...
5. "guava-concurrent-slides.pdf" 最后这份文档可能是关于Guava并发特性的幻灯片,涵盖了Guava在并发编程中的应用,比如Future、ListenableFuture、Service、Executor等工具类的使用。 通过阅读这些文档,读者可以...
Guava的学习笔记.docx文件可能包含了对以上概念的详细解释和示例代码,而未命名的压缩包子文件可能是实际的代码示例或者更深入的案例研究。通过阅读这些材料,你可以深入了解Guava库的用法,并将其应用于你的项目中...
Guava是Google提供的一个强大的Java库,提供了许多实用功能,例如集合框架的扩展、缓存、并发工具等。在处理空集合时,Guava提供了更安全的API。比如,Guava的`Optional<T>`类可以用来表示一个值可能存在也可能不...
SSM实战项目——Java高并发秒杀API是一个深入学习Java后端开发的重要实践,它涵盖了Spring、SpringMVC和MyBatis三大框架的整合应用,以及如何处理高并发下的秒杀场景。在这个项目中,我们将深入理解如何设计并实现一...
6、**限流**:在高并发场景下,限流策略可以保护系统稳定,RuoYi可能利用如Guava RateLimiter或Spring Cloud Gateway等工具实现。 7、**幂等性处理**:防止表单重复提交是幂等性的一个应用场景,通过令牌机制或唯一...
5. **并发编程**:并发工具类(如ConcurrentHashMap、Semaphore、CountDownLatch),以及并发容器(如ConcurrentLinkedQueue)的使用。 6. **分布式相关**:负载均衡、分布式缓存(如Redis)、分布式锁、消息队列...
Guava Joda-time Log4j Java 8 容器 Tomcat 1、Java 语言基础和JDK源码阅读 Java 语言基础 编号 名称 1 基本 2 运算符 3 数据类型 4 类 对象 接口 5 数组 6 枚举 7 异常处理 8 注解 9 范型 其他 编号 名称 1 Object...
例如,“对手分布函数库”可能指的是在Java中使用不同的库(如Apache Commons或Guava)来处理数据分布或算法;“空间不能快速的纪念版”可能是指内存管理或性能优化问题;“上课了半年”暗示了作者正在进行长期的...
此外,Guava库提供了更丰富的工具类,如Preconditions、Lists、Maps等,提高了开发效率。 以上五个部分构成了Java编程的基础,掌握这些知识点能够帮助开发者构建高效、稳定的系统。在实际工作中,结合具体需求,...
6. **Guava Cache**:Google的Guava库提供了强大的缓存功能,可能会详细讲解如何使用Guava Cache构建和管理缓存。 7. **JCache(JSR 107)**:Java标准的缓存API,介绍如何利用JCache接口进行缓存操作,以及它与...
笔记 Java 语言相关的API,第三方库和计算机基础理论等知识的学习和整理 更多 : | | 目录 资源 详细目录 Java语言和JDK源码 Java语言的基础理论知识 并发编程 Java并发编程相关的内容,并发包源码分析等 集合框架 ...
[笔记] dagger2: Retrofit和ButterKnife的注解怎么实现 反射 JOOR [ClassLoader==>字节码,apk] Socket==>openfire==>netty 设计模式:都在源码里 更好的使用java: [Json] Gson: [xml==>webservice] [Rxjava] ...
heavyz的Tomcat学习笔记(包括启动流程分析、启动脚本分析) ] () [ java8 新特性讲解系列文章 ] () [ Java 从Jar文件中动态加载类 ] () [ Java并发源码分析 - ThreadPoolExecutor ] () [ java ClassLoader 基本原理 ]...
它旨在帮助开发者通过Markdown格式整理和分享他们的学习笔记,同时也涵盖了其他技术领域如Vue.js、Node.js、Android学习以及Guava库的常用解析。 【描述】该项目可能是正在进行中的工作,因为提到了"可能未完结",...
Java语言的特点包括跨平台性(得益于"一次编写,到处运行"的JVM)、强大的库支持(如Apache Commons、Google Guava等)、以及丰富的生态系统(如Maven Central仓库)。在实际开发中,Java被广泛应用于Web应用、大...
这个压缩包中可能包含了大量的教程、示例代码、笔记文档和其他相关材料,为初学者和经验丰富的开发者提供了丰富的学习资源。 1. **基础语法**:Java的基础语法是学习的起点,包括数据类型(如基本类型和引用类型)...
在Java多线程的实践中,通过阅读和理解开源项目的源码,如Apache Commons Pool、Google Guava的线程池管理,可以帮助我们更好地理解和运用线程管理。同样,设计模式的源码实现,如Spring框架中的策略模式、工厂模式...
此外,Java还提供了线程同步机制,包括synchronized关键字、wait()和notify()方法以及Lock接口(如ReentrantLock),用于解决并发访问资源时可能出现的问题,如死锁、竞态条件和活锁等。理解线程生命周期、线程安全...