获取子线程的执行结果
- Runnable,不能返回一个返回值,不会抛出异常
- Callable,类似Runnable,但他可以返回一个返回值,也可以抛出异常
1、Future
- Future,可以用Future.get()接收Callable返回的线程执行结果
- Future,可以通过Future.isDone()来判断任务是否已经执行完了,以及取消这个任务,限时获取任务的结果等
- Future,在Callable的call()方法执行完毕之前,调用get()的线程(假设此时是主线程)会被阻塞,直到call()方法返回了结果后,此时future.get()才会得到该结果,然后主线程才会钱换到runnable(运行中)状态
- 所以Future是一个存储器,他存储了call()这个任物的结果,而这个任务的执行时间是无法提前确定的,因为这完全取决于call()方法执行的情况
(1)get()方法:获取结果
get方法的行为取决于Callable任务的状态,只有以下这5钟情况:
- 任务正常完成:get方法会立刻返回结果
- 任务尚未完成(任务还没开始或进行中):get将阻塞并直到任务完成
- 任务执行过程中抛出异常:get方法会抛出ExecutionException(这里的抛出异常,是call()执行时产生的那个异常),get方法抛出的异常类型都是他
- 任务被取消:get方法会抛出CancellationException
- 任务超时:get方法有一个重载方法,是传入一个延迟时间的,如果时间到了还没有获得结果,get方法会抛出TimeoutException
(2)get(long timeout, TimeUnit unit):有超时的获取
- get(long timeout, TimeUnit unit):如果call()在规定时间内完成任务,那么就会正常获取到返回值;而如果再指定时间内没有计算出结果,那么就会抛出TimeoutException
- 超时不获取,任务需要取消
(3)cancel方法:取消任务的执行
(4)isDone()方法:判断线程是否执行完毕
- 执行完毕不代表成功执行
- 比如说任务执行失败,但是也是任务执行完毕,会返回ture
- 比如说任务被中断了,也会返回true
(5)isCancelled()方法:判断是否被取消
演示
1、Future
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10);
//提交任务 Future<Integer> submit = executorService.submit(new CallableTesk());
//阻塞获取结果 Integer integer = submit.get(); System.out.println("integer = " + integer); executorService.shutdown(); } static class CallableTesk implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(1000); return new Random().nextInt(); } } }
优化
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10); Callable<Integer> callable = ()->{ Thread.sleep(1000); return new Random().nextInt(); }; //提交任务 Future<Integer> submit = executorService.submit(callable); //获取结果(阻塞) Integer integer = submit.get(); System.out.println("integer = " + integer); executorService.shutdown(); } }
2、批量接收结果
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(2); //创建结果集合 List<Future> list = new ArrayList<>(); //提交任务 for (int i = 0; i < 10; i++) { //提交任务 Future<Integer> submit = executorService.submit(new CallableTesk()); //加入结果集合 list.add(submit); } //获取结果 for (int i = 0; i < list.size(); i++) { Object o = list.get(i).get(); System.out.println(o); } executorService.shutdown(); } static class CallableTesk implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(1000); return new Random().nextInt(); } } }
3、执行异常和isDone
描述:演示get方法过程中抛出的异常,并不是一产生异常就抛出,而是直到执行get时,才会抛出。
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(2); //提交任务 Future<Integer> submit = executorService.submit(new CallableTesk()); Thread.sleep(500); //判断任务是否执行完毕 System.out.println("submit.isDone() = " + submit.isDone()); //获取结果 submit.get(); executorService.shutdown(); } static class CallableTesk implements Callable<Integer> { @Override public Integer call() throws Exception { throw new RuntimeException("抛出异常"); } } }
4、默认广告的超时和取消
描述:演示get的超时方法,需要注意超时后处理,调用future.cancel()。演示cancel传入true和false的区别,代表是否中断正在执行的任务。
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(2); //提交任务 Future<Integer> submit = executorService.submit(new CallableTesk()); //获取结果 try { submit.get(1000, TimeUnit.MICROSECONDS); } catch (TimeoutException e) { //超时,终止任务 submit.cancel(true); System.out.println("抛出TimeoutException"); } catch (ExecutionException ee) { System.out.println("抛出ExecutionException"); } catch (InterruptedException ie) { System.out.println("抛出InterruptedException"); } executorService.shutdown(); } static class CallableTesk implements Callable<Integer> { @Override public Integer call() { try { Thread.sleep(3000); System.out.println("进来了"); return 1; } catch (InterruptedException e) { System.out.println("抛出InterruptedException"); return 1; } } } }
结果
把cancel参数改为false
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(2); //提交任务 Future<Integer> submit = executorService.submit(new CallableTesk()); //获取结果 try { submit.get(1000, TimeUnit.MICROSECONDS); } catch (TimeoutException e) { //超时,终止任务 submit.cancel(false); System.out.println("抛出TimeoutException"); } catch (ExecutionException ee) { System.out.println("抛出ExecutionException"); } catch (InterruptedException ie) { System.out.println("抛出InterruptedException"); } executorService.shutdown(); } static class CallableTesk implements Callable<Integer> { @Override public Integer call() { try { Thread.sleep(3000); System.out.println("进来了"); return 1; } catch (InterruptedException e) { System.out.println("抛出InterruptedException"); return 1; } } } }
结果
说明cancel参数为true时,会终止线程,而false,还会继续执行线程任务。
5、cancel方法:取消任务的执行
(1)如果这个任务还没有开始执行,那么这种情况最简单,任务会被正常的取消,未来也不会被执行,方法返回true。
(2)如果任务已完成,或者已取消,那么cancel()方法会执行失败,方法返回false。
(3)如果这个任务已开始执行了,那么这个取消方法将不会直接取消该任务,而是会根据我们填的参数mayInterruptIfRunning做判断:
- true:取消任务
- false:如果任务正在执行,则会继续执行;如果任务还没开始执行,则该任务会被取消,未来也不会执行。
用FutureTask来创建Future
- 用FutureTask来获取Future和任务的结果
- FutureTask是一种包装器,可以把Callable转化成Future和Runnable,它同时实现两者的接口
public class Demo7 { public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<Integer> callable = ()-> { System.out.println("任务正在执行"); Random random = new Random(); Thread.sleep(2000); return random.nextInt(100); }; FutureTask<Integer> integerFutureTask = new FutureTask<>(callable); //把FutureTask当作Runnable来用即可 new Thread(integerFutureTask).start(); //当然,也可以把他放在线程池中 // ExecutorService executorService = Executors.newCachedThreadPool(); // executorService.submit(integerFutureTask); //获取任务结果 Integer integer = integerFutureTask.get(); System.out.println("运行结果integer = " + integer); } }