1、Runable的缺点
无法获取返回值
Runable中的run方法,无法抛出异常
2、Future和Callable的关系
2.1、Callable
有返回值,且可以抛出异常
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
2.2、Future作用
如果遇到一个耗时的方法,让子线程去执行,我不用等待,可以去做其他的事情,直到我想要获取结果的时候,再去获取
2.3、Callable和Future的关系
可以用Future的get方法获取接口的返回值,通过isDone来判断任务是否执行完毕,以及取消这个任务,或者限时获取任务的结果
在call方法未执行完之前,调用get方法,我们的主线程会进入阻塞,等待任务完成后,call方法才会返回结果,主线程才会切换回Runable状态
Future可以理解为一个存储器,存储了call方法返回的结果,任务执行的时间是无法预知的,这取决于call方法的执行状态
3、Future的主要方法
3.1、get
get最简单的使用方法
/** * @Classname OneFuture * @Description 演示一个简单的Future使用方法 * @Date 2021/5/23 16:05 * @Created by WangXiong */ public class OneFuture { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(5); Future<Integer> future = service.submit(new CallableTask()); try { Integer integer = future.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } service.shutdown(); } static class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { Thread.sleep(3000); return new Random().nextInt(); } } }
使用Lambda表达式
/** * @Classname OneFuture * @Description 使用lambda表达式来实现 * @Date 2021/5/23 16:05 * @Created by WangXiong */ public class OneFutureLambda { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(5); Callable<Integer> callable = ()->{ Thread.sleep(3000); return new Random().nextInt(); }; Future<Integer> future = service.submit(callable); try { Integer integer = future.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } service.shutdown(); } }
多个任务,用Future数组来获取结果
/** * @Classname MultiFutures * @Description 演示批量提交任务,用list来接受结果 * @Date 2021/5/23 16:19 * @Created by WangXiong */ public class MultiFutures { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(2); ArrayList<Future<Integer>> list = new ArrayList<>(); Callable<Integer> callable = ()->{ Thread.sleep(3000); return new Random().nextInt(); }; for (int i = 0; i < 20; i++) { Future<Integer> future = service.submit(callable); list.add(future); } for (int i = 0; i < list.size(); i++) { Future<Integer> future = list.get(i); try { Integer integer = future.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } service.shutdown(); } }
3.2、isDone
isDone会判断任务是否执行完毕
任务中抛出异常,isDone方法演示
- 只有调用get方法后,才可以感知到任务中抛出的异常
- 抛出的异常都是ExecutionException异常
- isDone,如果任务运行结束,会返回true
/** * @Classname GetExecption * @Description 演示Future中抛出异常,for循环是为了演示抛出的时机: * 不是一开始就抛出异常,直到get的时候才抛出异常 * @Date 2021/5/23 16:27 * @Created by WangXiong */ public class GetExecption { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(5); CallableTask task = new CallableTask(); Future<Integer> future = service.submit(task); for (int i = 0; i < 5; i++) { System.out.println(i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } try { System.out.println(future.isDone()); future.get(); } catch (InterruptedException e) { System.out.println("抛出异常InterruptedException"); e.printStackTrace(); } catch (ExecutionException e) { System.out.println("抛出异常ExecutionException"); e.printStackTrace(); } service.shutdown(); } static class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { throw new RuntimeException("抛出异常"); } } }
3.3、get(long timeout, TimeUnit unit)
演示get的超时方法
/** * @Classname TimeOut * @Description 演示get的超时时间,超时后需要处理,调用cancel,传入true和false的区别, * 代表是否中断正在执行的任务 * @Date 2021/5/23 18:54 * @Created by WangXiong */ public class TimeOut { private static final Ad DEFAULT_AD = new Ad("无网络时,默认广告"); private static final ExecutorService exec = Executors.newFixedThreadPool(10); public static void main(String[] args) { TimeOut timeOut = new TimeOut(); timeOut.printAd(); } public void printAd(){ Future<Ad> future = exec.submit(new FetchAdTask()); Ad ad = null; try { ad = future.get(2000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); ad = new Ad("被中断时候的默认广告"); } catch (ExecutionException e) { e.printStackTrace(); ad = new Ad("异常时候的默认广告"); } catch (TimeoutException e) { e.printStackTrace(); ad = new Ad("超时时候的默认广告"); //cancel()中传入true,代表直接中断 //cancel()中传入false,代表让线程执行完后中断 boolean cancel = future.cancel(true); System.out.println("cancel的值为:"+cancel); } exec.shutdown(); System.out.println(ad); } static class FetchAdTask implements Callable<Ad> { @Override public Ad call() throws Exception { try { Thread.sleep(3000); }catch (InterruptedException e){ System.out.println("sleep中被中断"); return new Ad("被中断时候的默认广告"); } return new Ad("订票哪家强?上东找南翔"); } } static class Ad{ private String name; public Ad(String name){ this.name = name; } @Override public String toString() { return "Ad{" + "name='" + name + '\'' + '}'; } } }
测试
3.4、cancel方法
cancel方法的代码演示可以看上面的代码演示
cancel方法执行的三种情况:
- cancel执行的任务还没有开始,任务会被正常取消,未来也不会执行,返回true
- 如果任务已经完成或取消,那么cancel执行会失败,返回false
- 如果任务执行到一半,会根据传入的参数进行处理,参数是true,会发出中断信号;如果传递的是false,则不会发送中断信号
4、用FutureTask来创建Future
FutureTask的实现关系,他同时实现了Runable和Future两个接口
代码演示
/** * @Classname FutureTaskDemo * @Description 演示FutureTask的demo * @Date 2021/5/23 23:13 * @Created by WangXiong */ public class FutureTaskDemo { public static void main(String[] args) { Task task = new Task(); FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(task); new Thread(integerFutureTask).start(); try { System.out.println("运行结果:"+integerFutureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } static class Task implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("子线程正在计算"); Thread.sleep(3000); Integer sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; } } }
测试
使用线程池
/** * @Classname FutureTaskDemo * @Description 演示FutureTask的demo * @Date 2021/5/23 23:13 * @Created by WangXiong */ public class FutureTaskDemo { public static void main(String[] args) { Task task = new Task(); FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(task); // new Thread(integerFutureTask).start(); //使用线程池 ExecutorService service = Executors.newCachedThreadPool(); service.submit(integerFutureTask); try { System.out.println("运行结果:"+integerFutureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } static class Task implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("子线程正在计算"); Thread.sleep(3000); Integer sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; } } }