一、Callable与Future
Runnable封装了一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。Callable与Runnable类似,但是有返回值。Callable接口是一个参数化的类型,只有一个方法call。
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; }
类型参数是返回值的类型。
Future保存异步计算的结果。可以启动一个计算,将Future对象交给某个线程,然后忘掉它。Future对象的所有者在结果计算好之后就可以获得它。
Future接口具有下面方法:
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
第一个get方法的调用被阻塞,直到计算完成。如果在计算完成之前,第二个方法的调用超时,抛出一个TimeoutException异常。如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。如果计算已经完成,那么get方法立即返回。
如果计算还在进行,isDone方法返回false;如果完成了,则返回true。
可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果mayInterrupt参数为true,它被中断。
FutureTask包装器是一种非常便利的机制,可将Callable转换成Future和Runnable,它同时实现两者的接口。
例如:
Callable<Integer> myComputation = ...; FutureTask<Integer> task = new FutureTask<>(myComputation); Thread t = new Thread(task); //it's a Runnable t.start(); ... Integer result = task.get(); //it's a Future
每一次对get的调用都会发生阻塞直到结果可获得为止。当然,线程时并行运行的,因此,很可能在大致相同的时刻所有的结果都可获得。
实例如下:
package com.thread.callable; import java.io.File; import java.util.Scanner; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class FutureTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("请输入目录:"); String directory = in.nextLine(); System.out.println("请输入关键字:"); String keyword = in.nextLine(); MatchCounter counter = new MatchCounter(new File(directory), keyword); FutureTask<Integer> task = new FutureTask<>(counter); Thread t = new Thread(task); t.start(); try { System.out.println(task.get()+" matching files."); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } in.close(); } }
package com.thread.callable; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class MatchCounter implements Callable<Integer> { private File directory; private String keyword; private int count; public MatchCounter(File directory, String keyword) { this.directory = directory; this.keyword = keyword; } @Override public Integer call() { count = 0; try { File[] files = directory.listFiles(); List<Future<Integer>> results = new ArrayList<>(); for (File file : files) { if (file.isDirectory()) { MatchCounter counter = new MatchCounter(file, keyword); FutureTask<Integer> task = new FutureTask<>(counter); results.add(task); Thread t = new Thread(task); t.start(); } else { if (search(file)) count++; } } for (Future<Integer> result : results) { try { count += result.get(); } catch (ExecutionException e) { e.printStackTrace(); } } } catch (InterruptedException e) { } return count; } public boolean search(File file) { try (Scanner in = new Scanner(file)) { boolean found = false; while (!found && in.hasNext()) { String line = in.nextLine(); if (line.contains(keyword)) return found = true; } return found; } catch (FileNotFoundException e) { e.printStackTrace(); return false; } } }
常用方法: V call() 运行一个将产生结果的任务
V get()
V get(long time,TimeUnit unit) 获取结果,如果没有结果可用,则阻塞直到真正得到结果超过指定时间为止。如果不成功,第二个方法会抛出TimeoutException异常。
FutureTask(Callable<V> task)
FutureTask(Runnable task,V result) 构造一个既是Funture<V>又是Runnable的对象。