参考博文:https://www.cnblogs.com/dolphin0520/p/3949310.html
前面的文章中我们讲述了多线程的三种实现方式:一种是继承Thread类;一种是实现Runnable接口;还有一种就是实现Callable接口。
前两种方式存在一个缺陷:线程体在执行完任务后无法获取执行结果。如果想要获取结果就要通过共享变量或者使用线程通信的方式来实现,这样使用起来比较麻烦。而自从JDK1.5之后,Java提供了Callable 和 Future 接口,通过它们可以在任务执行完毕之后得到任务的执行结果。接下来我们就讨论一下Callable、Future、TutureTask三个类(或接口)的使用方法。
一、Runnable 和 Callable
java.lang.Runnable(jdk1.0) 是一个接口,里面只声明了一个run() 方法:
package java.lang;
/**
* @author Arthur van Hoff
* @see java.lang.Thread
* @see java.util.concurrent.Callable
* @since JDK1.0
*/
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
由于run() 方法的返回值为 void 类型,所以在执行完任务之后无法返回任何结果。
Callable(jdk1.5)接口位于 JUC 包下,它也是一个接口,在它里面也只声明了一个方法 call():
package java.util.concurrent;
/**
* @see Executor
* @since 1.5
* @author Doug Lea
* @param <V> the result type of method {@code call}
*/
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
可以看出,这是一个泛型接口,call() 方法返回的类型就是传递进来的V类型。
那么怎么使用Callable 接口呢?一般情况下是配合JUC包中的 ExecutorService(jdk1.5) 接口来使用的,在 ExecutorService 接口中声明了若干个 submit 方法的重载版本:
package java.util.concurrent;
public interface ExecutorService extends Executor {
/**
* @since 1.5
* @author Doug Lea
*/
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
}
第一个submit() 方法里面的参数就是Callable。一般情况下我们使用第一个和第三个submit方法,第二个很少使用。
二、Future
Future 就是对具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get() 方法获取执行结果,该方法会阻塞直到任务返回结果。
Future接口位于JUC 包下:
package java.util.concurrent;
/**
* @see FutureTask
* @see Executor
* @since 1.5
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
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 个方法,下面依次解释每个方法的作用:
(1)boolean cancel(boolean mayInterruptIfRunning):
cancel方法用来取消任务,如果取消成功则返回 ture,如果取消失败则返回false。参数mayInterruptIfRunning 表示是否允许取消正在执行却没有执行完毕的线程任务,true表示允许取消,否则不允许。如果任务已经完成,则无论mayInterruptIfRunnaing 为ture还是false,此方法都返回false,即取消已经执行完成的任务返回false;如果任务还没有执行,则无论 mayInterruptIfRunning 为何值,都返回true。
(2)boolean isCancelled():
判断任务是否取消成功,成功返回true;
(3)boolean isDone():
判断任务是否已经执行完成,若任务完成返回true;
(4)V get():
该方法用于获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
(5)V get(long timeout, TimeUnit unit):
该方法用于获取执行结果,如果在指定时间内,还没有获取结果,就之间返回null;
也就是说 Future 提供了三种功能:
- 判断任务是否完成;
- 中断任务;
- 获取任务执行的结果;
三、FutureTask
(1)FutureTask简介
我们先来看一下FutureTask的实现
java.lang.Object
|____java.util.concurrent.FutureTask<V>
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
* @param <V> The result type returned by this FutureTask's {@code get} methods
*/
public class FutureTask<V> implements RunnableFuture<V> {}
FutureTask 类实现了 RunnableFuture 接口,我们看一下 Runnable 接口的实现:
package java.util.concurrent;
/**
* @see FutureTask
* @see Executor
* @since 1.6
* @author Doug Lea
* @param <V> The result type returned by this Future's {@code get} method
*/
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
RunnableFuture 接口继承了 Runnable 接口和 Future 接口,而FutureTask 实现了RunnableFuture 接口。所以它既可以作为 Runnable 被线程执行,也可以作为Future 得到 Callable 的返回值。
(2)构造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
FutureTask 提供了两个构造方法,一个接受Callable类型的参数,一个接受Callable类型和V(返回的结果)类型的参数;第二个构造方法借助Callable实现。
FutureTask是Future的唯一实现类(jdk1.6)。JDK1.7之后,ForkJoinTask也实现了Future。
四、使用示例
(1)使用Callable 接口和 Future获取执行结果。
任务类(实现Callable接口):
package basis.SuCallable;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class MyCallable implements Callable <Integer>{
@Override
public Integer call() throws Exception {
System.out.println("子线程(call)执行任务...");
TimeUnit.SECONDS.sleep(2);
int sum = 0;
for (int i = 0;i<100;i++){
sum += i;
}
return sum;
}
}
测试类:
package basis.SuCallable;
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
//创建一个执行器服务(线程池)
ExecutorService service = Executors.newCachedThreadPool();
//创建一个Callable类型的任务
MyCallable task = new MyCallable();
//向线程池提交该任务,并获取执行结果
Future<Integer> result = service.submit(task);
//关闭线程池
service.shutdown();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程(main)执行任务...");
//获取执行结果
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕。");
}
}
运行结果:
子线程(call)执行任务...
主线程(main)执行任务...
4950
所有任务执行完毕。
(2)使用Callable+FutureTask获取执行结果
任务类(MyCallable)不变。
测试类:
package basis.SuCallable;
import java.util.concurrent.*;
public class Test_1 {
public static void main(String[] args) {
//创建一个执行器服务(线程池)
ExecutorService service = Executors.newCachedThreadPool();
//创建一个Callable类型的任务
MyCallable task = new MyCallable();
//创建FutureTask类,传入任务。
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
//提交任务
service.submit(futureTask);
//关闭线程池
service.shutdown();
//第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
/*
MyCallable task_1 = new MyCallable();
//创建FutureTask类,传入任务。
FutureTask<Integer> futureTask_1 = new FutureTask<Integer>(task);
Thread thread = new Thread(futureTask_1);
thread.start();
*/
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程(main)执行任务...");
//获取执行结果
try {
System.out.println("运行结果:"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕。");
}
}
执行结果:
子线程(call)执行任务...
主线程(main)执行任务...
运行结果:4950
所有任务执行完毕。
补充:
Executors 是一个现成池的工具类,提供了许多静态方法,极大简化了现成池的创建和管理操作。
java.lang.Object
|____java.util.concurrent.Executors
package java.util.concurrent;
/**
* @since 1.5
* @author Doug Lea
*/
public class Executors {