JAVA多线程学习十-Callable与Future的应用
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的应用的更多相关文章
- java多线程系列(七)---Callable、Future和FutureTask
Callable.Future和FutureTask 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量 ...
- Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)
java多线程-概念&创建启动&中断&守护线程&优先级&线程状态(多线程编程之一)java多线程同步以及线程间通信详解&消费者生产者模式&死锁& ...
- Java多线程编程:Callable、Future和FutureTask浅析
通过前面几篇的学习,我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结 ...
- JAVA多线程提高七:Callable与Future的应用
Callable与Runnable 先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法: public interface Runnable { publ ...
- JAVA多线程学习十六 - 同步集合类的应用
1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...
- JAVA多线程学习十五 - 阻塞队列应用
一.类相关属性 接口BlockingQueue<E>定义: public interface BlockingQueue<E> extends Queue<E> { ...
- JAVA多线程学习十二 - Semaphere同步工具
java 中Semaphere可类比操作系统信号量,硬件资源如IO.内存.磁盘等都是有固定量的,多个程序需要竞争这些资源,没有资源就需要被挂起. 一.类和方法摘要 构造函数: public Semap ...
- 转:Java多线程学习(总结很详细!!!)
Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程 ...
- Java多线程学习笔记(一)——多线程实现和安全问题
1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...
随机推荐
- pytest之 fixture 实现机制
一.相同测试数据存放优化 在讲 fixture 实现机制之前,插入一段内容 上次有个小伙伴问我说,类似下面的用例代码情况,每条测试用例的数据都一样的,我们可以怎么进行优化吗? 当然是可以的 其实我们可 ...
- [CodeLife]记毕业后第一份工作
记毕业后第一份工作与公司 写在前面--前言 已然临近21年五月,很快又是一年毕业季了,公司里来了应届的新人,忽然才意识到自己已经不是公司年龄最小的了((笑~).依稀还记得两年前,自己也是如他们那般青涩 ...
- CS5211替代PS8625|设计EDP转LVDS转接屏方案|替代PS8625
PS8625将作为DP或eDP接收器设备出现在视频源中,并将作为LVDS显示面板的LVDS源设备.该设备是一个完全集成的解决方案,不需要外部CPU.内存.时钟基准或电压调节器.PS8625可配置为从显 ...
- javaScript系列 [09]-javaScript和JSON (拓展)
本文输出JSON搜索和JSON转换相关的内容,是对前两篇文章的补充. JSON搜索 在特定的开发场景中,如果服务器端返回的JSON数据异常复杂(可能超过上万行),那么必然就有对JSON文档进行搜索的需 ...
- windows环境下elasticsearch安装教程(超详细)
一.安装jdk ElasticSearch是基于lucence开发的,也就是运行需要java jdk支持.所以要先安装JAVA环境. 由于ElasticSearch 5.x 往后依赖于JDK 1.8的 ...
- 2.HTML5基本标签
一.标题标签 h1-->h6 h1最大 h6最小 <body> <h1>一级标题</h1> <h2>二级标题</h2> ...
- centos7 配置JDK
// 查看是否有jdk rpm -qa | grep java 卸载掉系统自带的jdk(箭头标识),命令:rpm -e --nodeps 后面跟系统自带的jdk名 比如:rpm -e --node ...
- PowerShell 教程
随笔分类 - 教程 转载自:https://www.cnblogs.com/XiaoCY/category/1065141.html PowerShell 管道符之Where-Object的使用方法 ...
- [Beyond Compare] 排除/忽略 .svn 文件夹
[Beyond Compare] Exclude .svn folders Beyond Compare 3 Session >> Session Settings... >> ...
- [Docker] 制作并运行 Nginx 镜像
环境 操作系统(cat /etc/redhat-release):CentOS Linux release 7.6.1810 (Core) Docker:18.09.6 文件 Dockerfile F ...