Future介绍
2020-08-15 本文已影响0人
张明学
在并发编程中,我们经常用到非阻塞的模型,不管是继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。
Future
Future有5个方法
/**
* 方法可以用来停止一个任务,如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false.
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 方法判断当前方法是否取消
*/
boolean isCancelled();
/**
* 方法判断当前方法是否完成
*/
boolean isDone();
/**
* 方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
*/
V get() throws InterruptedException, ExecutionException;
/**
* 做多等待timeout的时间就会返回结果
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
原来的做法
// 耗时5秒的复杂事务
public static String longTimeDoSomething() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "DoSomething-Result";
}
// 耗时2秒的复杂计算
public static Long complexCompute() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100L;
}
不开子线程处理
@Test
public void test1() {
Long startTime = System.currentTimeMillis();
// 耗时5秒的复杂事务
String result1 = Computer.longTimeDoSomething();
// 耗时2秒的复杂计算
Long result2 = Computer.complexCompute();
Long endTime = System.currentTimeMillis();
log.info("耗时={}毫秒,结果={},{}", (endTime - startTime), result1, result2);
}
[ main]耗时=7009毫秒,结果=DoSomething-Result,100
开两个子线程
@Test
public void test2() throws Exception {
Long startTime = System.currentTimeMillis();
Object[] objects = new Object[2];
Thread thread1 = new Thread(() -> {
String result1 = Computer.longTimeDoSomething();
log.info("thread1执行完毕,结果={}", result1);
objects[0] = result1;
});
thread1.start();
thread1.join();
Thread thread2 = new Thread(() -> {
Long result2 = Computer.complexCompute();
log.info("thread2执行完毕,结果={}", result2);
objects[1] = result2;
});
thread2.start();
thread2.join();
Long endTime = System.currentTimeMillis();
log.info("耗时={}毫秒,结果={},{}", (endTime - startTime), objects[0], objects[1]);
}
[Thread-1]thread1执行完毕,结果=DoSomething-Result [Thread-2]thread2执行完毕,结果=100 [ main]耗时=7083毫秒,结果=DoSomething-Result,100
为了得到thread1和thread2两个子线程的结果,必须得将两个子线程join(当前线程等待另一个调用join()方法的线程执行结束后再往下执行),这样可以保证主线程可以拿到子线程的结果。但是这样和同步操作也没有什么区别!
FutureTask
我们将子线程的任务交给FutureTask,然后开启一个子线程,再从FutureTask中获取结果。
FutureTask<V> implements RunnableFuture<V> RunnableFuture<V> extends Runnable, Future<V>
@Test
public void test3() throws Exception {
Long startTime = System.currentTimeMillis();
Object[] objects = new Object[2];
// FutureTask需要传入一个Callable接口
FutureTask<String> futureTask1 = new FutureTask(new Callable() {
@Override
public String call() throws Exception {
log.info("thread1开始执行");
String result1 = Computer.longTimeDoSomething();
log.info("thread1执行完毕,结果={}", result1);
return result1;
}
});
Thread thread1 = new Thread(futureTask1);
thread1.start();
FutureTask<Long> futureTask2 = new FutureTask(()->{
log.info("thread2开始执行");
Long result2 = Computer.complexCompute();
log.info("thread2执行完毕,结果={}", result2);
return result2;
});
Thread thread2 = new Thread(futureTask2);
thread2.start();
// 方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
objects[0] = futureTask1.get();
objects[1] = futureTask2.get();
Long endTime = System.currentTimeMillis();
log.info("耗时={}毫秒,结果={},{}", (endTime - startTime), objects[0], objects[1]);
}
[Thread-1]thread1开始执行
[Thread-2]thread2开始执行
[Thread-2]thread2执行完毕,结果=100
[Thread-1]thread1执行完毕,结果=DoSomething-Result
[ main]耗时=5011毫秒,结果=DoSomething-Result,100
thread1和thread2可以同时开始执行,最终也可以拿到两个结果。注意,如果将futureTask1.get()方法迁移到thread1.start()方法下面立即执行,就达不到这个效果,这样就和上面一样,串行执行