Callable与Runnable

先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:

public interface Runnable {
public abstract void run();
}

由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。 
Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法call():

public  interface  Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。 
一般情况下Callable是配合ExecutorService来使用的。ExecutorService接口中声明了若干个submit方法的重载版本:

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

一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。第一个submit方法里面的参数类型就是Callable。

Future

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

Future类位于java.util.concurrent包下,它是一个接口:

public  interface  Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

在Future接口中声明了5个方法,下面依次解释每个方法的作用:

  • cancel方法用来取消任务:

当你想要取消你已提交给执行者的任务,使用Future接口的cancel()方法。根据cancel()方法参数和任务的状态不同,这个方法的行为将不同:

  • 1、如果这个任务已经完成或之前的已被取消或由于其他原因不能被取消,那么这个方法将会返回false并且这个任务不会被取消。
  • 2、 如果这个任务正在等待执行者获取执行它的线程,那么这个任务将被取消而且不会开始它的执行。如果这个任务已经正在运行,则视方法的参数情况而定。 cancel()方法接收一个Boolean值参数(mayInterruptIfRunning)。如果参数为true并且任务正在运行,那么这个任务将被取消。如果参数为false并且任务正在运行,那么这个任务将不会被取消。

  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

  • isDone方法表示任务是否已经完成,若任务完成,则返回true;

  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:

1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

FutureTask

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

public class FutureTask<V> implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}

FutureTask提供了2个构造器,可以看到其实实现方式是一样的

public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
} public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}

事实上,FutureTask只是Future接口的实现类。

使用示例

1.使用Callable+Future获取执行结果

ExecutorService threadPool =  Executors.newSingleThreadExecutor();
Future<String> future =
threadPool.submit(
new Callable<String>() {
public String call() throws Exception {
Thread.sleep(1000);
return "hello";
};
}
);
System.out.println("....");
try {
System.out.println("get value:" + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}

2.使用Callable+FutureTask获取执行结果

Executor框架利用FutureTask来完成异步任务,并可以用来进行任何潜在的耗时的计算。一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*; public class TestThreadPool {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
List<FutureTask<String>> tasks = new ArrayList<FutureTask<String>>();
for (int i = 0; i < 10; i++) {
FutureTask<String> futureTask = new FutureTask<String>(new ThreadPoolTask(i));
threadPool.submit(futureTask);
tasks.add(futureTask);
}
for (FutureTask<String> futureTask : tasks) {
try {
// 阻塞一直等待执行完成拿到结果
System.out.println("future result:" + futureTask.get());
// 阻塞一直等待执行完成拿到结果,如果在超时时间内,没有拿到则抛出异常
// System.out.println("future result:"+futureTask.get(1,TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} // 捕获超时异常
// catch (TimeoutException e) {
// e.printStackTrace();
// }
}
System.out.println("--------------------------");
threadPool.shutdown();
} public static class ThreadPoolTask implements Callable<String> {
private int value;
public ThreadPoolTask(int value) {
this.value = value;
}
public String call() throws Exception {
System.out.println("value-----" + value++);
Thread.sleep(2000);
return String.valueOf(value);
}
}
}

对比使用Callable+Future的实现可知道,使用FutureTask不用去接收submit的返回值,而是自身就继承了Future,相对方便些。

CompletionService

示例

这里先给出个示例

import java.util.Random;
import java.util.concurrent.*; public class CallableAndFuture {
ExecutorService threadPool2 = Executors.newFixedThreadPool(10);
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool2);
for(int i=1;i<=10;i++){
final int seq = i;
completionService.submit(new Callable<Integer>() {
public Integer call() throws Exception {
Thread.sleep(new Random().nextInt(2000));
return seq;
}
});
}
for(int i=0;i<10;i++){
try {
System.out.println(
completionService.take().get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("======");
threadPool2.shutdown();
} }

使用Callable+Future和FutureTask的实现需要定义一个List来保存Future的值,取值得时候还需要通过for循环遍历,相对来说使用CompletionService更方便。


CompletionService方法

ExecutorCompletionService构造方法:

1、ExecutorCompletionService(Executor executor) 使用为执行基本任务而提供的执行程序创建一个 ExecutorCompletionService,并将 LinkedBlockingQueue 作为完成队列。

2、ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>>>completionQueue) 使用为执行基本任务而提供的执行程序创建一个 ExecutorCompletionService,并将所提供的队列作为其完成队列。

返回值 方法
Future<V> take()获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待。
Future<V> submit(Callable task)提交要执行的值返回任务,并返回表示挂起的任务结果的 Future。
Future<V> submit(Runnable task, V result)提交要执行的 Runnable 任务,并返回一个表示任务完成的 Future,可以提取或轮询此任务。
Future<V> poll()获取并移除表示下一个已完成任务的 Future,如果不存在这样的任务,则返回 null。
Future<V> poll(long timeout, TimeUnit unit)获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则将等待指定的时间(如果有必要)。

原理

CompletionService整合了Executor和BlockingQueue的功能。你可以将Callable任务提交给它去执行,然后使用类似于队列中的take和poll方法,在结果完整可用时获得这个结果,像一个打包的Future。ExecutorCompletionService是实现CompletionService接口的一个类,并将计算任务委托给一个Executor。

ExecutorCompletionService的实现相当直观。它在构造函数中创建一个BlockingQueue,用它去保持完成的结果。计算完成时会调用FutureTask中的done方法。当提交一个任务后,首先把这个任务包装为一个QueueingFuture,它是 FutureTask的一个子类,然后覆写done方法,将结果置入BlockingQueue中,take和poll方法委托给了BlockingQueue,它会在结果不可用时阻塞。

ExecutorCompletionService的一个构造函数,整合了Executor和BlockingQueue的功能:

public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}

任务的提交和执行都是委托给Executor来完成。当提交某个任务时,该任务首先将被包装为一个QueueingFuture,

public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}

QueueingFuture是FutureTask的一个子类,通过改写该子类的done方法,可以实现当任务完成时,将结果放入到BlockingQueue中。

private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}

而通过使用BlockingQueue的take或poll方法,则可以得到结果。在BlockingQueue不存在元素时,这两个操作会阻塞,一旦有结果加入,则立即返回。public Future<V> take() throws InterruptedException {

    return completionQueue.take();
} public Future<V> poll() {
return completionQueue.poll();
}
转载:java并发编程-Callable与Future

JAVA多线程提高七:Callable与Future的应用的更多相关文章

  1. java多线程系列(七)---Callable、Future和FutureTask

    Callable.Future和FutureTask 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量 ...

  2. Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)

    java多线程-概念&创建启动&中断&守护线程&优先级&线程状态(多线程编程之一)java多线程同步以及线程间通信详解&消费者生产者模式&死锁& ...

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

    通过前面几篇的学习,我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结 ...

  4. JAVA多线程学习十-Callable与Future的应用

    Callable与Runnable 先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法: public interface Runnable { publ ...

  5. paip.java 多线程参数以及返回值Future FutureTask 的使用.

    paip.java 多线程参数以及返回值Future FutureTask 的使用. 在并发编程时,一般使用runnable,然后扔给线程池完事,这种情况下不需要线程的结果. 所以run的返回值是vo ...

  6. Java并发编程:Callable、Future和FutureTask

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

  7. (转)Java并发编程:Callable、Future和FutureTask

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

  8. Java并发编程:Callable、Future和FutureTask(转)

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

  9. java多线程之Callable、Future和FutureTask

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

随机推荐

  1. Swing State: Consistent Updates for Stateful and Programmable Data Planes

    Swing State: Consistent Updates for Stateful and Programmable Data Planes 年份:2017 来源:ACM 本篇论文解决的问题 B ...

  2. UVA 11270 轮廓线

    题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=33787 题意: 用1*2或2*1的长条把n*m方格铺满的方案数. ...

  3. 单机安装 consul ui 对外提供服务

    Consul 安装启动ui,外网无法访问,应为Consul 默认绑定127.0.0.1 ,所以外网无法访问. 通过设置 -client 参数来设置     consul agent -server - ...

  4. Spring源码解析 – AnnotationConfigApplicationContext容器创建过程

    Spring在BeanFactory基础上提供了一些列具体容器的实现,其中AnnotationConfigApplicationContext是一个用来管理注解bean的容器,从AnnotationC ...

  5. 关于ADO一个容易被忽视的问题!UpdateBatch [问题点数:0分]

    这是一个常见但容易被忽视的问题,旧贴有问及但没答案,因此提高分数.相信大家常这样使用Cache模式:ADOConnection1.BeginTrans;Try ADODataSet1.UpdateBa ...

  6. mysql(五)查询缓存

    mysql的逻辑架构图如下: 当开启查询缓存时,mysql会将查询结果缓存到查询缓存区域,结果对应的key是使用查询语句,数据库名称,客户端协议的版本等因素算出的一个hash值. 在下次查询时,根据一 ...

  7. Ubuntu系统下adb devices 不能显示手机设备

    1. 查看usb设备,命令:lsusb 结果如下:Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub B ...

  8. LBP纹理特征[转自]

    LBP方法(Local binary patterns)是一个计算机视觉中用于图像特征分类的一个方法.LBP方法在1994年首先由T. Ojala, M.Pietikäinen, 和 D. Harwo ...

  9. CF486D-Valid Sets

    题目 给出一个\(n\)个点的树,每个点有权值\(a_i\),再给出一个\(d\),问有多少个非空点集满足: 点集在树上构成联通子图 \[\max _{v\in S}a_v -\min _{v\in ...

  10. Infinity NaN undefined和null

    Infinity属性用于存放表示正无穷大的数值. 负无穷大是表示负无穷大一个数字值. 该属性为Global对象的一个只读属性, 所有主流浏览器均支持该属性. Infinity属性的值为Number类型 ...