一、Future简介
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
当我们在使用线程池的时候,我们怎么知道阻塞队列的任务的执行情况呢?
Future为我们提供了可以获取线程返回值的方法。同时如果这个任务如果执行时间很长,我们可以在提交任务后立刻返回Future,主线程可以做其它事情,之后再去获取返回值。
Future的方法:
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;
}
- cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回true
- isDone方法表示任务是否已经完成,若任务完成,则返回true;
- get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
注:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(200);
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS, workQueue);
for(int i = 0; i < 5; i++) {
Future<String> future = executor.submit(new Task()); //提交任务执行返回Future,相当于给个取货单
System.out.println(future.get()); //使用取货单取货
}
executor.shutdown();
}
}
class Task implements Callable<String> {
@Override
public String call() throws Exception {
Random rd = new Random();
Thread.sleep(rd.nextInt(1000));
return Thread.currentThread().getName();
}
}
FutureTask
/**
* 1.6使用AQS实现,存在中断bug,线程池执行任务中断会影响其它任务
* JDK1.7,FutureTask 通过使用内部类Sync继承AQS来实现。
* JDK1.8没有使用AQS,而是自己实现了一个同步等待队列,在结果返回之前,所有的线程都被阻塞,存放到等待队列中。
*/
public interface RunnableFuture<V> extends Runnable, Future<V>
public class FutureTask<V> implements RunnableFuture<V> {
//构造函数
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // 状态值
}
//runnable - 可运行的任务 result - 成功完成时要返回的结果
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
//阻塞到线程完成获取返回值
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
}
FutureTask 和 Future的比较
FutureTask:
- 实现了Runnable和Future,所以兼顾两者优点。
- 它可以让程序员准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果
- FutureTask在高并发环境下确保任务只执行一次
主线程在submit提交task后,可以去完成其它工作,future.get方法可以获取执行结果返回值。
RunnableFuture接口继承了Future和Runnable,所以既可以作为Runnable执行又可以作为Future得到Callable的返回值
public class FutureTaskTest {
public static void main(String[] args) {
//创建线程池不推荐该写法,建议使用ThreadExecutor
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
FutureTask<String> futureTask = new FutureTask<>(new Task()); //每次创建一个FutureTask对象交给线程池
executorService.submit(futureTask);
try {
System.out.println(futureTask.get()); //上面是作为task,这里是Future,可以获取线程执行结果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
for(int i = 0; i < 5; i++) {
//基于FutureTask的扩张类
FutureTaskVo futureTaskVo = new FutureTaskVo(new Task());
executorService.submit(futureTaskVo);
try {
System.out.println(futureTaskVo.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService.shutdown();
}
}
class Task implements Callable<String> {
@Override
public String call() throws Exception {
Random rd = new Random();
Thread.sleep(rd.nextInt(1000));
return Thread.currentThread().getName();
}
}
class FutureTaskVo extends FutureTask<String> {
public FutureTaskVo(Callable<String> callable) {
super(callable);
}
//重写此方法可以看到每个线程是否执行完成
@Override
protected void done() {
try {
System.out.println("线程运行完毕 " + get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}