Guava ListenableFuture实现异步非阻塞调用
来源:互联网 发布:js .target方法 编辑:程序博客网 时间:2024/05/17 01:42
为了保证系统响应迅速,需要寻找一种方法能够使调取接口能够异步执行,而Java正好提供了类似的方法,在java.util.concurrent中包含了Future相关的类,运用其中的一些类可以进行异步计算,以减少主线程的等待时间。比如启动一个main方法,main中又包含了若干个其它任务,在不使用Java future的情况下,main方法中的任务会同步阻塞执行,一个执行完成后,才能去执行另一个;如果使用java future,则main方法中的任务会异步执行,main方法不用等待一个任务的执行完成,只需往下执行就行。一个任务的执行结果又该怎么获取呢?这里就需要用到Future接口中的isDone()方法来判断任务是否执行完,如果执行完成则可获取结果,如果没有完成则需要等待。 可见虽然主线程中的多个任务是异步执行,但是无法确定任务什么时候执行完成,只能通过不断去监听以获取结果,所以这里是阻塞的。这样,可能某一个任务执行时间很长会拖累整个主任务的执行。
前言
针对这样的情况,google对java.util.concurrent中的许多类进行封装,最终产生了google guava框架,其中com.google.common.util中的ListenableFuture就是本文要叙述的重点。查看com.google.common.util,发现其中的很多类都是对java.util.concurrent的封装,以增加特有的方法。ListenableFuture扩展了future方法,增加了addListener方法,该方法可以监听线程,并通过回调函数来获取结果,达到线程之间异步非阻塞执行。
首先,了解下同步、异步、阻塞、非阻塞相关概念;其次,简单介绍java future和guava future相关技术,并通过示例代码进一步对其进行理解;最后,对java future和guava future进行比较。
同步、异步、阻塞、非阻塞
同步
同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。
异步
异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
阻塞
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。
非阻塞
非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
Java Future
Java Future 的特点,可以理解为(减少主函数的等待时间,使得原本需要等待的时间段可以处理其它事情)
Executors创建线程池的几种常见方式
通过Executors可以创建不同类似的线程池,常见的大概有下表几种类型,还有些可能未被列出。在实际应用中,个人感觉主要使用newCachedThreadPool和newFixedThreadPool来创建线程池。
Executors创建线程池源码
//调用newCachedThreadPool方法,可以创建一个缓冲型线程池,而在改方法中通过传参创建一个ThreadPoolExecutor,我么你会很奇怪明明返回的是一个ExecutorService,怎么会创建了一个ThreadPoolExecutor呢?public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable());}// ThreadPoolExecutor继承了抽象的service类AbstractExecutorServicepublic class ThreadPoolExecutor extends AbstractExecutorService {}//AbstractExecutorService实现了ExecutorService接口public abstract class AbstractExecutorService implements ExecutorService {}//所以ExecutorService其实是ThreadPoolExecutor的基类,这也就解释清楚了
ExecutorService(线程池)
ExecutorService是一个接口,它继承了Executor,在原有execute方法的基础上新增了submit方法,传入一个任务,该方法能够返回一个Future对象,可以获取异步计算结果。
//ExecutorService继承了Executor,并扩展了新方法。public interface ExecutorService extends Executor { }//Executor中的方法void execute(Runnable command);//增加了submit方法,该方法传任务来获取Future对象,而Future对象中可以获取任务的执行结果<T> Future<T> submit(Callable<T> task);Future<?> submit(Runnable task);
Future(获取异步计算结果)
Future接口中有下表所示方法,可以获取当前正在执行的任务相关信息。
FutureTask
Executor框架利用FutureTask来完成异步任务,并可以用来进行任何潜在的耗时的计算。一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
FutureTask包装了Callable和Runnable接口对象,提供对Future接口的基本实现,开始、取消计算、查询计算是否完成、获取计算结果。仅当计算完成时才能检索结果,当计算没有完成时,该方法会一直阻塞直到任务转入完成状态。一旦完成计算,不能够重新开始或取消计算。通过Excutor(线程池)来执行,也可传递给Thread对象执行。如果在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
//通过传入任务来构造FutureTaskpublic FutureTask(Callable<V> callable) {}public FutureTask(Runnable runnable, V result) {}//FutureTask中同样有获取当前任务状态的方法public boolean isCancelled(){}public boolean isDone() {}public boolean cancel(boolean mayInterruptIfRunning) {}//FutureTask实现RunnableFuturepublic class FutureTask<V> implements RunnableFuture<V> {}//RunnableFuture继承Runnable和Futurepublic interface RunnableFuture<V> extends Runnable, Future<V>
示例代码
import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class TestFuture { // 创建线程池 final static ExecutorService service = Executors.newCachedThreadPool(); public static void main(String[] args) throws InterruptedException, ExecutionException { Long t1 = System.currentTimeMillis(); // 任务1 Future<Boolean> booleanTask = service.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return true; } }); while (true) { if (booleanTask.isDone() && !booleanTask.isCancelled()) { //模拟耗时 Thread.sleep(500); Boolean result = booleanTask.get(); System.err.println("BooleanTask: " + result); break; } } // 任务2 Future<String> stringTask = service.submit(new Callable<String>() { @Override public String call() throws Exception { return "Hello World"; } }); while (true) { if (stringTask.isDone() && !stringTask.isCancelled()) { String result = stringTask.get(); System.err.println("StringTask: " + result); break; } } // 任务3 Future<Integer> integerTask = service.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return new Random().nextInt(100); } }); while (true) { if (integerTask.isDone() && !integerTask.isCancelled()) { Integer result = integerTask.get(); System.err.println("IntegerTask: " + result); break; } } // 执行时间 System.err.println("time: " + (System.currentTimeMillis() - t1)); }}
Guava Future
Guava Future的特点,(减少主函数的等待时间,使得多任务能够异步非阻塞执行)
ListenableFuture是可以监听的Future,它是对java原生Future的扩展增强。Future表示一个异步计算任务,当任务完成时可以得到计算结果。如果希望计算完成时马上就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做会使得代码复杂,且效率低下。如果使用ListenableFuture,Guava会帮助检测Future是否完成了,如果完成就自动调用回调函数,这样可以减少并发程序的复杂度。
MoreExecutors
该类是final类型的工具类,提供了很多静态方法。例如listeningDecorator方法初始化ListeningExecutorService方法,使用此实例submit方法即可初始化ListenableFuture对象。
ListeningExecutorService
该类是对ExecutorService的扩展,重写ExecutorService类中的submit方法,返回ListenableFuture对象。
ListenableFuture
该接口扩展了Future接口,增加了addListener方法,该方法在给定的excutor上注册一个监听器,当计算完成时会马上调用该监听器。不能够确保监听器执行的顺序,但可以在计算完成时确保马上被调用。
FutureCallback
该接口提供了OnSuccess和OnFailuren方法。获取异步计算的结果并回调。
Futures
该类提供和很多实用的静态方法以供使用。
ListenableFutureTask
该类扩展了FutureTask类并实现ListenableFuture接口,增加了addListener方法。
实例代码
import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.Executors;import com.google.common.util.concurrent.FutureCallback;import com.google.common.util.concurrent.Futures;import com.google.common.util.concurrent.ListenableFuture;import com.google.common.util.concurrent.ListeningExecutorService;import com.google.common.util.concurrent.MoreExecutors;public class TestListenableFuture2 { // 创建线程池 final static ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); public static void main(String[] args) throws Exception { Long t1 = System.currentTimeMillis(); // 任务1 ListenableFuture<Boolean> booleanTask = service.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return true; } }); Futures.addCallback(booleanTask, new FutureCallback<Boolean>() { @Override public void onSuccess(Boolean result) { System.err.println("BooleanTask: " + result); } @Override public void onFailure(Throwable t) { } }); // 任务2 ListenableFuture<String> stringTask = service.submit(new Callable<String>() { @Override public String call() throws Exception { return "Hello World"; } }); Futures.addCallback(stringTask, new FutureCallback<String>() { @Override public void onSuccess(String result) { System.err.println("StringTask: " + result); } @Override public void onFailure(Throwable t) { } }); // 任务3 ListenableFuture<Integer> integerTask = service.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return new Random().nextInt(100); } }); Futures.addCallback(integerTask, new FutureCallback<Integer>() { @Override public void onSuccess(Integer result) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("IntegerTask: " + result); } @Override public void onFailure(Throwable t) { } }); // 执行时间 System.err.println("time: " + (System.currentTimeMillis() - t1)); }}
Java Future 和 Guava Future的对比
Future 具有局限性。在实际应用中,当需要下载大量图片或视频时,可以使用多线程去下载,提交任务下载后,可以从多个Future中获取下载结果,由于Future获取任务结果是阻塞的,所以将会依次调用Future.get()方法,这样的效率会很低。很可能第一个下载速度很慢,则会拖累整个下载速度。
Future主要功能在于获取任务执行结果和对异步任务的控制。但如果要获取批量任务的执行结果,从上面的例子我们已经可以看到,单使用 Future 是很不方便的。其主要原因在于:一方面是没有好的方法去判断第一个完成的任务;另一方面是 Future的get方法 是阻塞的,使用不当会造成线程的浪费。第一个问题可以用 CompletionService 解决,CompletionService 提供了一个 take() 阻塞方法,用以依次获取所有已完成的任务。第二个问题可以用 Google Guava 库所提供的 ListeningExecutorService 和 ListenableFuture 来解决。除了获取批量任务执行结果时不便,Future另外一个不能做的事便是防止任务的重复提交。要做到这件事就需要 Future 最常见的一个实现类 FutureTask 了。Future只实现了异步,而没有实现回调,主线程get时会阻塞,可以轮询以便获取异步调用是否完成.
在实际的使用中建议使用Guava ListenableFuture来实现异步非阻塞,目的就是多任务异步执行,通过回调的方方式来获取执行结果而不需轮询任务状态
参考博客地址
http://blog.csdn.net/pistolove/article/details/51232004#t2
- Guava ListenableFuture实现异步非阻塞调用
- 从Java future 到 Guava ListenableFuture实现异步调用
- Guava之ListenableFuture(实现Java异步)
- guava ListenableFuture
- guava ListenableFuture
- ListenableFuture异步多线程代码实现
- iOS GCD 实现异步非阻塞
- PHP实现多线程 异步 非阻塞
- C# 异步非阻塞的接口实现
- ListenableFuture in Guava
- guava ListenableFuture使用
- ListenableFuture in Guava
- Google Guava ListenableFuture入门
- Guava 中的 ListenableFuture
- Guava ListenableFuture 小试牛刀
- ListenableFuture in Guava
- guava中的ListenableFuture
- ListenableFuture in Guava
- java——覆盖equals方法没有想象中那么简单
- make(9)
- 计算机文件读写原理
- tinyxml2解析RSS文件 并将其转换为.dat格式
- nexus-3.2.0-01.zip安装及使用说明
- Guava ListenableFuture实现异步非阻塞调用
- Javascript模块化编程
- 51nod 1577 异或凑数
- ios 绘图
- Mac系统中安装JDK
- 【机器学习小白日记】 Ubuntu16.04+Caffe+GTX1070+CUDA8.0+OpenCV3.2安装
- c++ map/multimap,set/multiset的使用(续)
- 循环获取json中key为变量时所对应的值
- Work深度剖析