接上篇博客
java线程——三种创建线程的方式 ,这篇博客主要介绍第三种方式Callable和Future。比较继承Thread类和实现Runnable接口,接口更加灵活,使用更广泛。但这两种方式都没有返回值,要想返回相应的数据,就要使用Callable和Future方式。 基础:1、Callable
还是从定义开始,Callable接口有返回值,并且可以抛出异常。
1.
2.
/**(有返回值的任务,可能抛出异常)
3.
* A task that returns a result and may throw an exception.
4.
* Implementors define a single method with no arguments called
5.
* {@code call}.
6.
7.
* @see Executor
8.
* @since 1.5
9.
* @author Doug Lea
10.
* @param <V> the result type of method {@code call}
11.
*/
12.
13.
public interface Callable<V> {
14.
15.
V call() throws Exception;
16.
}
17.
2、Future
Future同样也是一个接口,主要方法如下,方法的功能比较容易理解,所以就没有写注释。主要作用:获取任务执行结果,中断任务等。
1.
2.
package java.util.concurrent;
3.
4.
public interface Future<V> {
5.
6.
7.
boolean cancel(boolean mayInterruptIfRunning);
8.
9.
10.
boolean isCancelled();
11.
12.
13.
boolean isDone();
14.
15.
16.
V get() throws InterruptedException, ExecutionException;
17.
18.
19.
V get(long timeout, TimeUnit unit)
20.
throws InterruptedException, ExecutionException, TimeoutException;
21.
}
22.
3、FutureTask
1.
2.
public class FutureTask<V> implements RunnableFuture<V> {
3.
......
4.
}
5.
6.
public interface RunnableFuture<V> extends Runnable, Future<V> {
7.
......
8.
}
9.
FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable和Future。也就是说,它既可以作为Runnable被线程执行,也可以作为Future得到Callable返回值。
方法一:Callable+Future
1.
2.
public class CallableAndFuture {
3.
/**
4.
* 实现Callable接口
5.
*
6.
* @author YANG
7.
*
8.
*/
9.
public static class MyCallable implements Callable {
10.
11.
private int flag = 0;
12.
13.
public MyCallable(int flag) {
14.
15.
this.flag = flag;
16.
17.
}
18.
19.
// 重写call方法
20.
public String call() throws Exception {
21.
// 情况一:flag=0 返回0
22.
if (this.flag == 0) {
23.
24.
return "flag = 0";
25.
26.
}
27.
// 情况二:flag=1 返回looping 陷入死循环
28.
if (this.flag == 1) {
29.
30.
try {
31.
32.
while (true) {
33.
34.
System.out.println("looping.");
35.
36.
Thread.sleep(2000);
37.
38.
}
39.
// 情况三:出现异常
40.
} catch (InterruptedException e) {
41.
42.
System.out.println("Interrupted");
43.
44.
}
45.
46.
return "false";
47.
48.
} else {
49.
50.
throw new Exception("Bad flag value!");
51.
52.
}
53.
54.
}
55.
56.
}
57.
58.
public static void main(String[] args) {
59.
60.
// 定义3个Callable类型的任务,构造方法中制定flag的值
61.
MyCallable task1 = new MyCallable(0);
62.
63.
MyCallable task2 = new MyCallable(1);
64.
65.
MyCallable task3 = new MyCallable(2);
66.
67.
// 创建一个执行任务的服务
68.
69.
ExecutorService es = Executors.newFixedThreadPool(3);
70.
71.
try {
72.
73.
// 提交并执行任务,任务启动时返回了一个Future对象,
74.
75.
// 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
76.
77.
Future future1 = null;
78.
79.
future1 = es.submit(task1);
80.
81.
// 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
82.
83.
System.out.println("task1: " + future1.get());
84.
85.
Future future2 = es.submit(task2);
86.
87.
// 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环
88.
89.
Thread.sleep(5000);
90.
91.
System.out.println("task2 cancel: " + future2.cancel(true));
92.
93.
// 测试抛出异常
94.
95.
Future future3 = es.submit(task3);
96.
97.
System.out.println("task3: " + future3.get());
98.
99.
} catch (Exception e) {
100.
101.
System.out.println(e.toString());
102.
103.
}
104.
105.
// 停止任务执行服务
106.
107.
es.shutdownNow();
108.
109.
}
110.
}
111.
执行结果:
方法二:Callable+FutureTask
分析两种方法不同之处就在于Future和FutureTask,其中一个是接口,一个是类。因此,只有main方法调用部分不同,上面的MyCallable类中的内容保持不变。
1.
2.
public static void main(String[] args) {
3.
4.
MyCallable task1 = new MyCallable(0);
5.
FutureTask ft1 = new FutureTask(task1);
6.
MyCallable task2 = new MyCallable(1);
7.
FutureTask ft2 = new FutureTask(task2);
8.
MyCallable task3 = new MyCallable(2);
9.
FutureTask ft3 = new FutureTask(task3);
10.
11.
try {
12.
//启动task1
13.
new Thread(ft1, "子线程").start();
14.
System.out.println(ft1.get());
15.
16.
17.
//等待5秒后,停止task2
18.
new Thread(ft2, "子线程").start();
19.
Thread.sleep(5000);
20.
System.out.println("task2 cancel:" + ft2.cancel(true));
21.
22.
//启动task3
23.
new Thread(ft3, "子线程").start();
24.
System.out.println("task3:" + ft3.get());
25.
} catch (InterruptedException | ExecutionException e) {
26.
27.
System.out.println(e.toString());
28.
}
29.
30.
}
31.
其执行结果与方法一完全相同,对比这两种方式,第二种比较容易读懂,第一种相对困难些。下篇博客我们介绍Executor、ExecutorService等内容,相信之后理解起来就会很轻松了。