我们先来看一下FutureTask的实现:
1
public class FutureTask<V> implements RunnableFuture<V>
FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
1
2
3
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:
1
2
3
4
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
事实上,FutureTask是Future接口的一个唯一实现类。
可以把FutureTask交给Executor执行;也可以通ExecutorService.submit(…)方法返回一个FutureTask,然后执行FutureTask.get()方法或FutureTask.cancel(…)方法。除此以外,还可以单独使用FutureTask。 当一个线程需要等待另一个线程把某个任务执行完后它才能继续执行,此时可以使用FutureTask。假设有多个线程执行若干任务,每个任务最多只能被执行一次。当多个线程试图同时执行同一个任务时,只允许一个线程执行任务,其他线程需要等待这个任务执行完后才能继续执行。下面是对应的示例代码。
private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<>(); private String executionTask(final String taskName)throws ExecutionException, InterruptedException { while (true) { Future<String> future = taskCache.get(taskName); // 1.1,2.1 if (future == null) { Callable<String> task = () -> taskName; FutureTask<String> futureTask = new FutureTask<>(task); future = taskCache.putIfAbsent(taskName, futureTask); // 1.3 if (future == null) { future = futureTask; futureTask.run(); // 1.4执行任务 } } try { return future.get(); // 1.5, } catch (CancellationException e) { taskCache.remove(taskName, future); } } }public class ThreadTest { public static void main(String[] args){ //使用FutureTask来包装Callable对象 //使用Lambda表达式来创建Callable<Integer>对象 FutureTask<Integer> task = new FutureTask<>(()->{ System.out.println(Thread.currentThread().getName() + " " + "开始执行任务!"); return 0; }); //实质还是以Callable对象来创建并启动线程 //输出 新线程 开始执行任务! new Thread(task,"新线程").start(); try{ //获取线程的返回值 //输出 0 System.out.print(task.get()); }catch (Exception ex){ ex.printStackTrace(); } } }
jdk1.8的FutureTask有个说明: 修订说明:这与这个类以前依赖AbstractQueuedsynchronizer的版本不同,主要是为了避免在取消竞争期间保留中断状态让用户感到意外。在当前的设计中,Sync控件依赖于通过CAS更新的“state”字段来跟踪完成,以及一个简单的Treiber堆栈来保存等待的线程。 风格注意:与往常一样,我们绕过了使用 AtomicXFieldUpdaters的开销,而是直接使用Unsafe。
FutureTask实现了Future接口,Future接口有5个方法: 1、boolean cancel(boolean mayInterruptIfRunning) 尝试取消当前任务的执行。如果任务已经取消、已经完成或者其他原因不能取消,尝试将失败。如果任务还没有启动就调用了cancel(true),任务将永远不会被执行。如果任务已经启动,参数mayInterruptIfRunning将决定任务是否应该中断执行该任务的线程,以尝试中断该任务。 如果任务不能被取消,通常是因为它已经正常完成,此时返回false,否则返回true 2、boolean isCancelled() 如果任务在正常结束之前被被取消返回true 3、boolean isDone() 正常结束、异常或者被取消导致任务完成,将返回true 4、V get() 等待任务结束,然后获取结果,如果任务在等待过程中被终端将抛出InterruptedException,如果任务被取消将抛出CancellationException,如果任务中执行过程中发生异常将抛出ExecutionException。 5、V get(long timeout, TimeUnit unit) 任务最多在给定时间内完成并返回结果,如果没有在给定时间内完成任务将抛出TimeoutException。
FutureTask有以下7中状态: FutureTask任务的运行状态,最初为NEW。运行状态仅在set、setException和cancel方法中转换为终端状态。在完成过程中,状态可能呈现出瞬时值INTERRUPTING(仅在中断运行程序以满足cancel(true)的情况下)或者COMPLETING(在设置结果时)状态时。从这些中间状态到最终状态的转换使用成本更低的有序/延迟写,因为值是统一的,需要进一步修改。 state:表示当前任务的运行状态,FutureTask的所有方法都是围绕state开展的,state声明为volatile,保证了state的可见性,当对state进行修改时所有的线程都会看到。 NEW:表示一个新的任务,初始状态 COMPLETING:当任务被设置结果时,处于COMPLETING状态,这是一个中间状态。 NORMAL:表示任务正常结束。 EXCEPTIONAL:表示任务因异常而结束 CANCELLED:任务还未执行之前就调用了cancel(true)方法,任务处于CANCELLED INTERRUPTING:当任务调用cancel(true)中断程序时,任务处于INTERRUPTING状态,这是一个中间状态。 INTERRUPTED:任务调用cancel(true)中断程序时会调用interrupt()方法中断线程运行,任务状态由INTERRUPTING转变为INTERRUPTED
可能的状态过渡: 1、NEW -> COMPLETING -> NORMAL:正常结束 2、NEW -> COMPLETING -> EXCEPTIONAL:异常结束 3、NEW -> CANCELLED:任务被取消 4、NEW -> INTERRUPTING -> INTERRUPTED:任务出现中断