Java并发编程:Future接口、FutureTask类
在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。
这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
今天我们就来讨论一下Callable、Future和FutureTask三个类的使用方法。以下是本文的目录大纲:
一.Callable与Runnable
二.Future
三.FutureTask
四.使用示例
若有不正之处请多多谅解,并欢迎批评指正。
请尊重作者劳动成果,转载请标明原文链接:
http://www.cnblogs.com/dolphin0520/p/3949310.html
一.Callable与Runnable
先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:
|
1
2
3
|
public interface Runnable { public abstract void run();} |
由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。
Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():
|
1
2
3
4
5
6
7
8
9
|
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方法的重载版本:
|
1
2
3
|
<T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task); |
第一个submit方法里面的参数类型就是Callable。
暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。
一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。
二.Future
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future类位于java.util.concurrent包下,它是一个接口:
|
1
2
3
4
5
6
7
8
|
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方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
- isDone方法表示任务是否已经完成,若任务完成,则返回true;
- get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
三.FutureTask
我们先来看一下FutureTask的实现:
|
1
|
public class FutureTask<V> implements RunnableFuture<V> |
FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
|
1
2
3
|
public interface RunnableFuture<V> extends Runnable, Future<V> { void run();} |
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:
|
1
2
3
4
|
public FutureTask(Callable<V> callable) {}public FutureTask(Runnable runnable, V result) {} |
事实上,FutureTask是Future接口的一个唯一实现类。
四.使用示例
1.使用Callable+Future获取执行结果
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public class Test { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); Task task = new Task(); Future<Integer> result = executor.submit(task); executor.shutdown(); try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("主线程在执行任务"); try { System.out.println("task运行结果"+result.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("所有任务执行完毕"); }}class Task implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("子线程在进行计算"); Thread.sleep(3000); int sum = 0; for(int i=0;i<100;i++) sum += i; return sum; }} |
执行结果:
2.使用Callable+FutureTask获取执行结果
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
public class Test { public static void main(String[] args) { //第一种方式 ExecutorService executor = Executors.newCachedThreadPool(); Task task = new Task(); FutureTask<Integer> futureTask = new FutureTask<Integer>(task); executor.submit(futureTask); executor.shutdown(); //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread /*Task task = new Task(); FutureTask<Integer> futureTask = new FutureTask<Integer>(task); Thread thread = new Thread(futureTask); thread.start();*/ try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("主线程在执行任务"); try { System.out.println("task运行结果"+futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("所有任务执行完毕"); }}class Task implements Callable<Integer>{ @Override public Integer call() throws Exception { System.out.println("子线程在进行计算"); Thread.sleep(3000); int sum = 0; for(int i=0;i<100;i++) sum += i; return sum; }} |
如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。
Java并发编程:Future接口、FutureTask类的更多相关文章
- java并发编程:线程安全管理类--原子操作类--AtomicInteger
在java并发编程中,会出现++,--等操作,但是这些不是原子性操作,这在线程安全上面就会出现相应的问题.因此java提供了相应类的原子性操作类. 1.AtomicInteger
- 并发编程-Future+callable+FutureTask 闭锁机制
项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们需要用callable实现. FutureTask ...
- Java并发编程笔记之FutureTask源码分析
FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...
- java并发编程:线程安全管理类--原子包--java.util.concurrent.atomic
java.util.concurrent.atomic 的描述 AtomicBoolean 可以用原子方式更新的 boolean 值. AtomicInteger 可以用原子方式更新的 int 值. ...
- JAVA并发编程学习笔记------FutureTask
FutureTask是Future和Callable的结合体.传统的代码是这样写的Future f = executor.submit(new Callable()); 然后通过Future来取得计算 ...
- Java并发编程笔记之Unsafe类和LockSupport类源码分析
一.Unsafe类的源码分析 JDK的rt.jar包中的Unsafe类提供了硬件级别的原子操作,Unsafe里面的方法都是native方法,通过使用JNI的方式来访问本地C++实现库. rt.jar ...
- java并发编程:线程安全管理类--原子操作类--AtomicBoolean
1.类AtomicBoolean
- java并发编程:线程安全管理类--原子操作类--AtomicLong
可以用原子方式更新的 long 值.有关原子变量属性的描述,请参阅 java.util.concurrent.atomic 包规范.AtomicLong 可用在应用程序中(如以原子方式增加的序列号), ...
- java并发编程:线程安全管理类--原子操作类--AtomicStampedReference<V>
1.类 AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新. 实现注意事项.通过创 ...
随机推荐
- 从今天开始学习C#啦
此博客为证,在下从今天开始学习C#,并把心得体会记录下来.
- Bootstrap Modal 框 alert confirm loading
/** * Created by Administrator on 2016/5/4. */ /** * 模态窗口 */ window.Modal = { tpls:{ alert:'<div ...
- (转)dedecms入门
学dedecms一段时间了,把我的入门体会和大家分享一下. 什么是dedecm cms(内容管理系统):现在有各种内容模型,如书评(包括书名,出版社,评论等字段).cms一般有用户后台,网页的用户可以 ...
- 使用Instant Client配置PL/SQL Developer
之前使用PL/SQL Developer都是直接在本机安装完整版的Oracle Database,一是省事,二是可以在本机做一些demo测试:最近换了台电脑,感觉Instant Client更简单一些 ...
- Xcode 的正确打开方式——Debugging(转)
转自CocoaChina http://www.cocoachina.com/ios/20150225/11190.html 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不 ...
- ACE的Socket初步
Tcp通信过程一般为如下步骤: 服务器绑定端口,等待客户端连接. 客户端通过服务器的ip和服务器绑定的端口连接服务器. 服务器和客户端通过网络建立一条数据通路,通过这条数据通路进行数据交互. 常用AP ...
- 关于#include后面<>和" "的区别
1.以尖括号制定头文件,如下所示: #include <stdio.h> 用尖括号来制定文件时,预处理器是以特定的方式来寻找文件,一般是环境中或编译器命令行指定的某种寻找路径.这种设置寻找 ...
- C语言零移位操作
给定一个整形数组要求把其中的零元素移动到数组的末尾 非零元顺序保持不变 以下采用两种方法实现 #include <stdlib.h> #include <stdio.h> #i ...
- Oracle高级查询,事物,过程及函数
一 数值函数 数值 abs,ceil,floor,round,trunc字符串 instr,substr SQL>SELECT 'ABS':'|| ABS(-12.3) FROM DUAL; 运 ...
- 3 Longest Substring Without Repeating Characters(最长不重复连续子串Medium)
题目意思:求字符串中,最长不重复连续子串 思路:使用hashmap,发现unordered_map会比map快,设置一个起始位置,计算长度时,去减起始位置的值 eg:a,b,c,d,e,c,b,a,e ...