Callable(可呼叫的,任务)&Future(未来,期望的)
接下来我们讲Callable和Future,这个Callable和Future的作用就是,我们程序启动一个线程,然后那个线程运行完了以后呢,它可以给我们返回结果。我们可以去获得这个线程执行完了以后的结果。以下通过代码来看一看它们的效果:
提交单个Callable线程任务,获得它的返回值
线程池不用线程池的void execute(Runnable command)方法来执行任务,而是用<T> Future<T> submit(Callable<T> task) 方法来提交任务,这个方法专门用于提交一个有返回值的任务。这个任务用Callable对象表示,重写它里面的call方法,就可以把代码放到call方法里面执行,并返回结果。结果的类型是通过泛型声明的。这个任务提交以后会有一个结果,这个结果用Future类型的变量接收。我们可以通过future的get()方法,获取到线程返回的结果。
public class CallableAndFuture { /** * @param args */ public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); Future<String> future = threadPool.submit(new Callable<String>() { public String call() throws Exception { Thread.sleep(2000); return "hello"; }; }); System.out.println("等待结果"); try { System.out.println("拿到结果:" + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } |
这个只是目前还不知道有什么使用场景。我有一个任务,交给你去运行,运行完了我确实可以拿到结果,但是,我什么时候能够拿到结果呢?我过一会儿就问一下,有没有结果。这对我来说就没有什么意思,那我还不如不起一个线程,我直接调用一个方法,不开子线程,然后执行,执行完了我得到结果。如果说我启动一个线程的意思就是说,好,你去干,干了以后呢,我忙我的,你忙你的,结果出来了,你告诉我,如果我一天到晚去问你,我还干不干事了呢?那我还不如直接调用你,没必要搞一个新线程。
简单的说,就是有一个Callable的任务,这个任务会返回结果,返回的结果用Future来拿,当结果返回了之后,Future就可以拿到结果了,当future还没有拿到结果的时候,future.get()会一直在那里等,既然是一直在那里等,代码没有办法往下执行,那我干脆调用一个方法,也是一样的,反正也是干不了活,都得等执行完,拿到结果。
好,那没有一种可能就是,我不想等,我跑过来看一下结果来了没有,没来的话,我又去忙别的,过了一会儿,我又来看结果来了没有,那么这时候我们可以看一下api文档,Future对象上有没有一种方法可以,我先去看有没有结果,如果没有结果,我就去忙别的,而不是必须在那里等。我们看到有一个get的重载方法V get(long timeout, TimeUnit unit),这个方法可以设定等待的时间。如果它是说超过等待的时间没有返回结果,我就可以去做别的事,这还比较好。如果超过时间没有结果就抛异常的话,这就受不了了。结果它是抛的一个Timeout异常。这就不行了,我的本意事没有就没有呗,没有我就去干别的事,结果呢,它非要去跟人家打一架,大的头破血流都报红了。这样,感觉就没有什么太大的意义。
CompletionService提交一组Callable任务,并获得它们的返回值
接下来我们再看一种应用CompletionService,就是我一下就提交一组Callable任务,然后我就用它的返回值类型CompletionService的take方法去取,凡是只要有一个任务运行完了,我就会得到结果。哪个任务先执行完,就先拿到哪个任务的结果。如果有10个任务,就要拿10次结果。它的泛型就是任务的返回值的类型。我们用CompletionService的子类ExecutorCompletionService来创建对象,这个创建的对象它不是一个线程池,它执行的时候,比如它有10个任务需要运行的时候,它是不是要交给线程池来执行呀。所以呢,我们还要给它的构造方法传入一个线程池对象。
ExecutorService threadPool2 = Executors.newFixedThreadPool(10); CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool2); for (int i = 1; i <= 10; i++) { final int seq = i; completionService.submit(new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(new Random().nextInt(5000)); return seq; } }); } for (int i = 0; i < 10; i++) { try { System.out.println(completionService.take().get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } |