创建线程的两种方式
- 直接继承
Thread
- 实现
Runnable
接口
这两种方式都有一个缺点:在执行完成任务之后,无法直接获取到最后的执行结果。如果需要获取执行结果,就必须通过共享变量或线程通信的方式来达到想要的效果,较为麻烦。
所以从 Java 1.5 起,就提供了两种方式:Callable
和 Future
,通过它们可以在任务执行结束后得到任务执行结果。
Runnable 与 Callable
首先是 java.lang.Rannable
,它是一个接口,里面只声明了一个 run()
方法:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
由于 run()
方法的返回值类型为 void
,所以在线程执行完后无任何返回结果。
然后是 java.util.concurrent.Callable
,它也是一个接口,也只声明了一个方法,其名为 call()
:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
可以看到,这是一个泛型接口,返回的类型就是传进来的 V
类型。
那么,如何使用 Callable
呢?一般是结合 ExecutorService
来使用。ExecutorService
声明了几种 submit
方法,其中有一个就是传入 Callable
:
<T> Future<T> submit(Callable<T> task);
Future
Future
声明了对具体的 Runnable
或者 Callable
任务执行进行取消、查询、结果获取等方法。必要时可以通过 get
方法获取执行结果,该方法会阻塞直到任务返回结果。
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
提供了三种功能:
- 取消任务:参数表示是否允许中途取消(中断)
- 判断状态:是否已取消、是否已完成
- 获取结果:两种方式,指不指定时间
因为 Future
只是一个接口,所以无法直接用来创建对象,因此有了下面的 FutureTask
。
FutureTask
首先看下 FutureTask
的继承关系:
可以看出 RunnableFuture
继承了 Runnable
接口和 Future
接口,而 FutureTask
实现了 RunnableFuture
接口。所以它既可以作为 Runnable
被线程执行,又可以作为 Future
得到 Callable
的返回值。
然后可以看到 FutureTask
内部有这几种状态:
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
再根据注释,可以得知当创建一个 FutureTask
对象时,初始状态是 NEW
,在运行过程中,运行状态仅在方法set
,setException
和 cancel
中转换为终端状态。有四种状态转换过程:
-
NEW -> COMPLETING -> NORMAL
:正常执行并返回结果(run
执行成功再设置状态为COMPLETING
) -
NEW -> COMPLETING -> EXCEPTIONAL
:执行过程中出现异常(setException
先设置状态为COMPLETING
) -
NEW -> CANCELLED
:执行前被取消 -
NEW -> INTERRUPTING -> INTERRUPTED
:执行时被中断(cancel
参数为true
才可能出现这个状态)
最后来看下 FutureTask
的两个构造器:
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
}
可知传入的任务最后都是付给内部的 private Callable<V> callable
。
事实上,FutureTask
是 Future
接口的一个唯一实现类。
更多关于 FutureTask
的源码分析,可以看 FutureTask源码解析 这篇文章。
使用示例
Future
第一种方式是使用继承了 ExecutorService
的线程池 ThreadPoolExecutor
中的 submit
方法,将 Callable
直接提交创建 Future
。
import java.util.concurrent.*;
public class FutureExample {
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("do something in callable");
Thread.sleep(5000);
return "Ok";
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new MyCallable());
System.out.println("do something in main");
Thread.sleep(1000);
String result = future.get();
System.out.println("result: " + result);
}
}
FutureTask
第二种方法是创建一个写好 Callable
的 FutureTask
对象实例,再 submit
。因为 FutureTask
内部本身拥有 run
方法,也可以直接创建线程 Thread
运行。
利用 ExecutorService
import java.util.concurrent.*
public class FutureTaskWithExecutorService {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> futureTask = new FutureTask<>(() -> { // java 8 函数式写法
System.out.println("do something in callable");
Thread.sleep(5000);
return "Ok";
});
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(futureTask);
System.out.println("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
System.out.println("result: " + result);
}
}
利用 Thread
import java.util.concurrent.*
public class FutureTaskWithThread {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("do something in callable");
Thread.sleep(5000);
return "Ok";
}
});
new Thread(futureTask).start();
System.out.println("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
System.out.println("result: " + result);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。