文章目录
前言
本篇文章介绍Callable、Future、FutureTask、CompletionService的基本使用。创建线程任务的三种方式,之前两种都是Runnable,第三种就是使用Callable。
Callable相比于Runnable的好处来说,使用Callable可以阻塞的获取到线程返回结果,而不用再通过等待唤醒去获取Runnable结果,而在Android中就是使用Handler来包装该返回结果。
本文简单了解这几个类,使用场景很多,待学,之后补上。
1. 基本接口介绍
// 执行Callable返回结果V
public interface Callable<V> {
V call() throws Exception;
}
// 操作执行结果
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;
}
/*
实现了Runnable和Future,即可以被线程启动、也可以被线程池启动,也可以获取返回结果
FutureTask即是它的实现类,看下源码便知道get()方法是怎样实现的,也是通过等待唤醒,即是阻塞的,使用时要注意
*/
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
// RunnableFuture的实现类,封装Callable或Runnable,在run()里执行callable.call,使用等待唤醒获取返回值。因为实现了Runnable接口,所以也可以用Thread直接启动
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
// ...
}
public FutureTask(Runnable runnable, V result) {
// ...
}
}
// 线程池
public abstract class AbstractExecutorService implements ExecutorService {
// 封装callable
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
// 封装callable
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
// 提交Runnable,
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
// 提交Runnable,result表示泛型
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
// 提交Callable
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
}
2. Callable、Future、FutureTask基本使用
2.1 使用Callable
Callable不能通过线程来启动,毕竟Thread类没有该构造方法。通过线程池submit
后返回Future对象,用于获取结果。实际上线程池返回的是FutureTask对象,只不过返回了接口而已。
private static void test() {
ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor();
Future<String> result = threadPoolExecutor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("子线程执行1");
Thread.sleep(2000);
int sum = 0;
for (int i = 0; i < 10000; i++) {
sum += i;
}
System.out.println("子线程执行2");
return String.valueOf(sum);
}
});
result.get(); // 阻塞获取返回值
}
2.2 使用FutureTask
private static void test() {
ExecutorService threadPoolExecutor = Executors.newSingleThreadExecutor();
// 创建FutureTask
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("子线程执行1");
Thread.sleep(2000);
int sum = 0;
for (int i = 0; i < 10000; i++) {
sum += i;
}
System.out.println("子线程执行2");
return String.valueOf(sum);
}
});
// 通过线程池启动
Future<?> submit = threadPoolExecutor.submit(futureTask);
try {
System.out.println("submit: " + submit.get()); // 无结果
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 通过线程启动,两者只能选用一个
// new Thread(futureTask).start();
futureTask.get(); // 阻塞获取返回值
}
3. ExecutorCompletionService使用
future执行的缺点: 一大推扔进线程池中,需要遍历获取结果,如果有慢的线程一直在阻塞,则后面先执行完的线程也要被阻塞。使用ExecutorCompletionService可以避免该缺点,执行完了优先返回。
/*
使用ExecutorCompletionService,谁快谁先出来
主线程 start
子线程执行: 1, pool-1-thread-2
子线程执行: 0, pool-1-thread-1
子线程执行: 3, pool-1-thread-4
子线程执行: 5, pool-1-thread-4
子线程执行: 7, pool-1-thread-5
子线程执行: 9, pool-1-thread-6
主线程获取执行结果: 0, 1
主线程获取执行结果: 1, 0
主线程获取执行结果: 2, 3
主线程获取执行结果: 3, 5
主线程获取执行结果: 4, 7
主线程获取执行结果: 5, 9
子线程执行: 2, pool-1-thread-3
主线程获取执行结果: 6, 2
子线程执行: 4, pool-1-thread-2
主线程获取执行结果: 7, 4
子线程执行: 6, pool-1-thread-1
主线程获取执行结果: 8, 6
子线程执行: 8, pool-1-thread-4
主线程获取执行结果: 9, 8
*/
private static void testExecutorCompletionService() {
ExecutorService executor = Executors.newCachedThreadPool();
ExecutorCompletionService<String> executorCompletionService = new ExecutorCompletionService<>(executor);
int count = 10;
for (int i = 0; i < count; ++i) {
// 使用ExecutorCompletionService提交任务
executorCompletionService.submit(new NumberCallable(i));
}
try {
for (int i = 0; i < count; ++i) {
// 使用ExecutorCompletionService#take()获取结果
System.out.println("主线程获取执行结果: " + i + ", " + executorCompletionService.take().get());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/*
普通执行,按顺序执行:
主线程 start
子线程执行: 0, pool-1-thread-1
子线程执行: 1, pool-1-thread-2
子线程执行: 3, pool-1-thread-1
子线程执行: 5, pool-1-thread-1
子线程执行: 7, pool-1-thread-1
子线程执行: 9, pool-1-thread-1
主线程获取执行结果: 0, 0
主线程获取执行结果: 1, 1
子线程执行: 2, pool-1-thread-2
主线程获取执行结果: 2, 2
主线程获取执行结果: 3, 3
子线程执行: 4, pool-1-thread-3
主线程获取执行结果: 4, 4
主线程获取执行结果: 5, 5
子线程执行: 6, pool-1-thread-4
主线程获取执行结果: 6, 6
主线程获取执行结果: 7, 7
子线程执行: 8, pool-1-thread-5
主线程获取执行结果: 8, 8
主线程获取执行结果: 9, 9
*/
private static void testFutures() {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future<String> submit = executorService.submit(new NumberCallable(i));
list.add(submit);
}
try {
for (int i = 0; i < list.size(); i++) {
System.out.println("主线程获取执行结果: " + i + ", " + list.get(i).get());
}
} catch (Exception e) {
// do nothing
}
executorService.shutdown();
}
结语:
Runnable使用最多,但Callable、Future、FutureTask这些也会用到,在某些场景下会省去不少事,不需再使用等待唤醒等方法去获取结果,在这些场景下可能会事半功倍。