Java 1.5开始, 提供了 Callable
和 Future
, 通过它们可以在任务执行完毕之后得到任务执行结果.
当需要调用几个执行很慢的方法时, 可以使用多线程一起执行这几个方法, 等所有方法执行完毕后得到执行结果, 在进行别的处理.
Future
接口主要包括 5 个方法:
get()
方法可以当任务结束后返回一个结果, 如果调用时, 工作还没有结束, 则会阻塞线程, 直到任务执行完毕.
get(long timeout,TimeUnit unit)
做多等待 timeout
的时间就会返回结果.
cancel(boolean mayInterruptIfRunning)
方法可以用来停止一个任务.
isDone()
方法判断当前方法是否完成.
isCancel()
方法判断当前方法是否取消.
需求场景: 等早餐过程中, 包子需要 3 秒, 凉菜需要 1 秒, 普通的多线程需要四秒才能完成. 先等凉菜, 再等包子, 因为等凉菜时, 普通多线程启动 start()
方法, 执行 run()
中具体方法时, 没有返回结果, 所以如果要等有返回结果, 必须是要1秒结束后才知道结果.
public static void main(String[] args) throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); // 等凉菜 Callable ca1 = new Callable() { @Override public String call() throws Exception { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "凉菜准备完毕"; } }; FutureTask<String> ft1 = new FutureTask<String>(ca1); new Thread(ft1).start(); // 等包子 -- 必须要等待返回的结果,所以要调用join方法 Callable ca2 = new Callable() { @Override public Object call() throws Exception { try { Thread.sleep(1000 * 3); } catch (InterruptedException e) { e.printStackTrace(); } return "包子准备完毕"; } }; FutureTask<String> ft2 = new FutureTask<String>(ca2); new Thread(ft2).start(); System.out.println(ft1.get()); System.out.println(ft2.get()); long end = System.currentTimeMillis(); System.out.println("准备完毕时间:" + (end - start)); }
还有一个比较典型的例子就是设置超时时间:
//固定大小的线程池,同时只能接受5个任务 static ExecutorService mExecutor = Executors.newFixedThreadPool(5); final static long timeout = 4 ; /** * 模拟在预定时间内获取广告信息 * @throws InterruptedException */ static void rederPageWithAd(final String pageTitle) throws InterruptedException{ Future<String> f = mExecutor.submit(new Callable<String>() { @Override public String call() throws Exception { System.out.println("开始加载广告信息"); int randomTime = new Random().nextInt(5) + 1;//限制耗时不会出现0s,不会大于10s Thread.sleep(100 * 1000); System.out.println("正常加载广告耗时:" + randomTime +"s"); return pageTitle; } }); String page; try { //在预计时间内等待 System.out.println("预期任务执行完时间:" + timeout + "s"); //page = f.get(); page = f.get(timeout, TimeUnit.SECONDS); } catch (ExecutionException e) { page = "出现执行异常,显示默认的广告页面"; } catch (TimeoutException e) { page = "任务执行超时,显示默认的广告页面"; f.cancel(true);//取消没有执行完的任务,设置为ture说明任务能被中断,否则执行中的任务要完成 } System.out.println("成功加载广告页面:" + page); } public static void main(String[] args) { try { List<String> titleList = new ArrayList<String>(); titleList.add("体育赛事"); titleList.add("娱乐新闻"); titleList.add("实时聚焦"); titleList.add("国际咨询"); titleList.add("影视天下"); titleList.add("游戏风云"); for (String string : titleList) { rederPageWithAd(string); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ /** * 只有执行了shutdown方法,执行isTerminated才有效。否则isTerminated一直为ture */ mExecutor.shutdown(); while(true){ if(mExecutor.isTerminated()){ System.out.println("所有任务都执行完了,关闭线程池"); break; } } } }
值得注意的是: 当主线程调用 Future
的 get
方法的时候会获取到从线程中返回的结果数据. 如果在线程的执行过程中发生了异常, get
会获取到异常的信息.