Runnable和Callable用法解析
Runnable
概述
众所周知,在Java中创建线程的2种方式是,继承thread重写run方法,实现Runnable接口。你可曾听闻有第三种方式叫做callable?先来探讨一下前两种方式是怎么实现的。Runnable接口是对异步任务的抽象,Thread对象实现了Runnable接口,并提供了多个构造方式用来创建线程,以下是Runnable接口的源码和Thread类的部分源码。
@FunctionalInterfacepublic interface Runnable {
public abstract void run();
}
public class Thread implements Runnable { private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
public synchronized void start() {
boolean started = false;
try {
start0();//native method
started = true;
}
}
//忽略其余代码
}
用法示例
通过thread的源码,我们可以得知:
- 在构造方式传入Runnable对象,调用start()方法可以创建线程,这种方式使用了默认的run()方法,并调用target的run()方法。
new Thread(new Runnable() { @Override
public void run() {
}
}).start();
- 通过继承Thread重写run()方法,调用start()方法可以创建线程,这种方式省去了把异步代码委托在Runnable对象中。
new Thread(){ @Override
public void run() {
}
}.start();
Callable
/** * A task that returns a result and may throw an exception.
* Implementors define a single method with no arguments called
* {@code call}.
*
* <p>The {@code Callable} interface is similar to {@link
* java.lang.Runnable}, in that both are designed for classes whose
* instances are potentially executed by another thread. A
* {@code Runnable}, however, does not return a result and cannot
* throw a checked exception.
*
* <p>The {@link Executors} class contains utility methods to
* convert from other common forms to {@code Callable} classes.
*
* @see Executor
* @since 1.5
* @author Doug Lea
* @param <V> the result type of method {@code call}
*/
@FunctionalInterface
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;
}
概述
从源码和注释得知,Callable也是一种类似Runnable的异步任务抽象,但是可以返回结果,或抛出异常的,也属于jdk1.5版本加入的Executor任务执行模块的一部分。我们知道Runnable是配合Thread类使用的,那么Callable呢?从Thread类源码中,并没有看到接受callable接口的参数,所以callable的不会与Thread直接联系使用,答案是FutureTask,FutureTask包含传入Callable和Runnable接口的构造方法,及判断是否执行完成,取消任务等接口,get()方法用于获取结果,并且是阻塞的,也就是说任务没有执行完成会一直等待。下面是FutureTask的继承关系和方法列表:
使用场景
FutureTask可用于异步任务获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接结合Thread对象或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。比如在需要聚合接口的场景,可以通过FutureTask并行远程调用多个接口,最后在主线程汇总结果返回给前端。
用法示例
最简单的用法
public void test1() { FutureTask<String> stringFutureTask = new FutureTask<>(() -> {
Thread.sleep(1000);
return "200 ok";
});
new Thread(stringFutureTask).start();
try {
System.out.println("等待获取结果");
System.out.println(stringFutureTask.get());
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
等待获取结果200 ok
配合线程池用法
public void test2() throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Future> futureTasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskNum = i;
//异步批量提交任务执行
Future<String> task = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);//模拟任务执行
return "200 ok" + taskNum;
}
});
//保存结果钩子
futureTasks.add(task);
System.out.println("任务" + taskNum + "添加到队列。");
}
//模拟主线程执行
Thread.sleep(2000);
futureTasks.forEach(future -> {
try {
//获取异步任务结果
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
}
任务0添加到队列。任务1添加到队列。
任务2添加到队列。
任务3添加到队列。
任务4添加到队列。
任务5添加到队列。
任务6添加到队列。
任务7添加到队列。
任务8添加到队列。
任务9添加到队列。
200 ok0
200 ok1
200 ok2
200 ok3
200 ok4
200 ok5
200 ok6
200 ok7
200 ok8
200 ok9
使用CompletionService
public static void test3() throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(10);
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
for (int i = 0; i < 10; i++) {
final int taskNum = i;
completionService.submit(() -> "200 ok" + taskNum);
}
for (int i = 0; i < 10; i++) {
Future<String> take = completionService.take();
System.out.println(take.get());
}
}
200 ok1200 ok0
200 ok2
200 ok3
200 ok4
200 ok5
200 ok6
200 ok7
200 ok8
200 ok9
以上是 Runnable和Callable用法解析 的全部内容, 来源链接: utcz.com/z/518051.html