Future和FutureTask实现异步计算

开发Android APP经常会使用AsyncTask框架来异步加载资源或者异步到服务器拉消息,等任务完成后再主动更新结果到UI主线程,AsyncTask框架可以非常方便的获取线程异步执行结果。Java 5之前,Java 并没有提供API用于查询线程是否执行完毕以及如何获取线程执行的结果;Java 5 之后版本提供的并发框架包java.util.concurrent对多线程提供了更多更好的支持,Future接口和FutureTask类是异步执行任务的框架的重要组成部分,为了更清楚的理解,我们还得从Runnable、Callable、ExecutorService等接口说起。

Runnable接口:instance可以使用new Thread(Runnable r)放到一个新线程中跑,没有返回结果;也可以使用ExecutorService.submit(Runnable r)放到线程池中跑,返回结果为null,等于没有返回结果,但可以通过返回的Future对象查询执行状态。

public abstract void run();

Callable接口:instance只能在ExecutorService的线程池中跑,但有返回结果,也可以通过返回的Future对象查询执行状态。表面上可以把Callable接口简单的理解为有返回结果的Runnalbe接口。

V call() throws Exception;

ExecutorService接口:线程池执行调度框架

<T> Future<T> submit(Callable<T> task);

<T> Future<T> submit(Runnable task, T result);

Future<?> submit(Runnable task);

Future接口:用于查询任务执行状态,获取执行结果,或者取消未执行的任务。在ExecutorService框架中,由于使用线程池,所以Runnable与Callable实例都当做任务看待,而不会当做“线程”看待,所以Future才有取消任务执行等接口。接口中的get()方法用于获取任务执行结果,因为任务是异步执行的,所以我们可以在需要使用结果的时候才调用get()方法,调用时如果任务还未执行完就会阻塞直到任务完成;当然我们也可以调用get的另一重载版本get(long timeout, TimeUnit unit),当阻塞时会等待指定的时间,如果时间到而任务还未完成,那么就会抛出TimeoutException。

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

FutureTask类:集Runnable、Callable、Future于一身,它首先实现了Runnable与Future接口,然后在构造函数中还要注入Callable对象(或者变形的Callable对象:Runnable + Result),所以FutureTask类既可以使用new Thread(Runnable r)放到一个新线程中跑,也可以使用ExecutorService.submit(Runnable r)放到线程池中跑,而且两种方式都可以获取返回结果,但实质是一样的,即如果要有返回结果那么构造函数一定要注入一个Callable对象,或者注入一个Runnable对象加一个预先给定的结果(个人觉得这作用不大)。

public interface RunnableFuture<V> extends Runnable, Future<V>{
...}

public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable)
public FutureTask(Runnable runnable, V result)
...
}

示例代码:

package com.stevex.app.forkjoin;

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.FutureTask;
import java.util.concurrent.TimeUnit;

public class FutureTaskTest {

	public static void main(String[] args) {
		Callable<String> c = new Callable<String>() {
			public String call() {
				try {
					TimeUnit.SECONDS.sleep(new Random().nextInt(5));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				return "Callable--"+Thread.currentThread().getName();
			}
		};

		//seed a single thread
		FutureTask<String> ft1 = new FutureTask<String>(c);
		Thread t = new Thread(ft1);
		t.start();

		Runnable r = new Runnable() {
			public void run() {
				try {
					TimeUnit.SECONDS.sleep(new Random().nextInt(5));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};

		FutureTask<String> ft2 = new FutureTask<String>(r, "Runnable");//give return value directly
		FutureTask<String> ft3 = new FutureTask<String>(c);
		FutureTask<String> ft4 = new FutureTask<String>(c);
		FutureTask<String> ft5 = new FutureTask<String>(c);
		FutureTask<String> ft6 = new FutureTask<String>(c);

		ExecutorService es = Executors.newFixedThreadPool(2);//init ExecutorService
		es.submit(ft2);
		es.submit(ft3);
		es.submit(ft4);
		es.submit(ft5);
		es.submit(ft6);

		try {
			TimeUnit.SECONDS.sleep(1);

			if(ft1.isDone()){
				ft4.cancel(false);

				if(ft4.isCancelled()){
					System.out.println("task4 cancelled.");
				}
			}

			if(ft2.isDone()){
				ft5.cancel(false);

				if(ft5.isCancelled()){
					System.out.println("task5 cancelled.");
				}
			}

			if(ft3.isDone()){
				ft6.cancel(false);

				if(ft6.isCancelled()){
					System.out.println("task5 cancelled.");
				}
			}

			System.out.println("task1 retult:" + ft1.get());
			System.out.println("task2 retult:" + ft2.get());
			System.out.println("task3 retult:" + ft3.get());

			if(! ft4.isCancelled()){
				System.out.println("task4 retult:" + ft4.get());
			}

			if(! ft5.isCancelled()){
				System.out.println("task5 retult:" + ft5.get());
			}

			if(! ft6.isCancelled()){
				System.out.println("task6 retult:" + ft6.get());
			}	

			es.shutdown();//shut down ExecutorService
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}

	}
}
时间: 2024-08-27 00:51:13

Future和FutureTask实现异步计算的相关文章

13.FutureTask异步计算

FutureTask     1.可取消的异步计算,FutureTask实现了Future的基本方法,提供了start.cancel 操作,可以查询计算是否完成,并且可以获取计算     的结果.结果只可以计算完成之后去获取,get方法会阻塞当前计算没有完成的线程,一定计算完成则会立即释放. 线程池submit与execute     1.submit()可以传入参数为实现callable接口的实例,返回future实例对象.     2.execute()返回void. package dem

Java并发编程实践:Callable异步回调Future、FutureTask用法

Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到.FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这

java异步计算Future的使用(转)

从jdk1.5开始我们可以利用Future来跟踪异步计算的结果.在此之前主线程要想获得工作线程(异步计算线程)的结果是比较麻烦的事情,需要我们进行特殊的程序结构设计,比较繁琐而且容易出错.有了Future我们就可以设计出比较优雅的异步计算程序结构模型:根据分而治之的思想,我们可以把异步计算的线程按照职责分为3类: 1. 异步计算的发起线程(控制线程):负责异步计算任务的分解和发起,把分解好的任务交给异步计算的work线程去执行,发起异步计算后,发起线程可以获得Futrue的集合,从而可以跟踪异步

Callable、Future和FutureTask浅析

我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结果该如何实现呢?还记上一篇Executor框架结构中提到的Callable接口和Future接口吗?,是的,从JAVA SE 5.0开始引入了Callable和Future,通过它们构建的线程,在任务执行完成后就可以获取执行结果,今天我们就来聊聊线程创建的第三种方式,那就是实现Callable接口. 1.Callabl

Java多线程21:多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask

CyclicBarrier 接着讲多线程下的其他组件,第一个要讲的就是CyclicBarrier.CyclicBarrier从字面理解是指循环屏障,它可以协同多个线程,让多个线程在这个屏障前等待,直到所有线程都达到了这个屏障时,再一起继续执行后面的动作.看一下CyclicBarrier的使用实例: public static class CyclicBarrierThread extends Thread { private CyclicBarrier cb; private int sleep

Java并发编程:Callable、Future和FutureTask的实现

启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从Java 1.5开始提供的Callable和Future下面就分析一下Callable.Future以及FutureTask的具体实现及使用方法源码分析基于JDK 1.7 一.Callable 与 Runnable java.lang.Runnable是一个接口,只有一个run()方法 public interface Runnable {    public abstract void run(); } run()方法的返

Java多线程编程:Callable、Future和FutureTask浅析

通过前面几篇的学习,我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结果该如何实现呢?还记上一篇Executor框架结构中提到的Callable接口和Future接口吗?,是的,从Java SE 5.0开始引入了Callable和Future,通过它们构建的线程,在任务执行完成后就可以获取执行结果,今天我们就来聊聊线程创建的第三种方式,那就是实现Callable接口.

Java并发编程之Callable,Future,FutureTask

创建线程: 1. 继承Thread 2. 实现Runnable 仔细观察会发现void run() ,这个方法没有返回值,也就无法获得线程执行结果. Callable 返回结果并且可能抛出异常的任务.实现者定义了一个不带任何参数的叫做 call 的方法. Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的.但是 Runnable 不会返回结果,并且无法抛出经过检查的异常. V call() 计算结果,如果无法计算结果,则抛出一个异常. 跟Runna

疯狂Java学习笔记(66)-----------Callable、Future和FutureTask

在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callable.Future和FutureTask三个类的使用方法.以下是本文的目录