在多线程开发中,我们如果想获取子线程的执行结果需要使用Callable和Future。
为什么需要Callable和Future Runnable的缺点:没有返回值也不能抛出Checked Exception。
Callable和Future的关系 JDK1.5开始,提供了一个能返回线程执行结果的接口Callable,需要定义子类去实现Callable接口。接口定义如下:
1 2 3 4 5 6 7 8 9 10 11 @FunctionalInterface public interface Callable <V > { V call () throws Exception ; }
Future也是一个接口。用于表示异步计算的结果,我们可以用Future.get()来获取Callable接口返回的结果,还可以用Future.isDone()来判断任务是否已经执行完毕,以及取消任务,限时获取任务结果等。所以Future是一个存储器,它存储了call()方法这个任务的结果,而这个任务的执行时间是无法确定的,这取决于call()方法的执行情况。
Callable和Future的用法 Future的get()方法可以获取线程的结果的行为取决于Callable.call()方法的状态,这几种状态如下:
任务正常完成:get立即返回结果。
任务尚未完成(任务未开始或进行中):get将阻塞直到任务完成。
任务执行过程中抛出Exception:get会抛出ExecutionException。
任务被取消:get会抛出CancellationException。
get(long timeout, TimeUnit unit)在指定时间内没有获取到结果,会抛出TimeoutException。
我们可以使用线程池的submit()方法来提交Callable任务,submit()方法定义如下:
1 2 <T> Future<T> submit (Callable<T> task) ;
Future获取结果的示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.util.Random;import java.util.concurrent.*;public class OneFuture { public static void main (String[] args) { ExecutorService service = Executors.newFixedThreadPool(10 ); Future<Integer> future = service.submit(() -> { Thread.sleep(1000 ); return new Random().nextInt(); }); try { System.out.println("线程执行结果:" + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } service.shutdown(); } }
任务产生了异常不会马上就抛出,直到get()执行时才会抛出。isDone()只会关心任务是否执行完成,而不会关心任务执行结果的好坏。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class GetException { public static void main (String[] args) { ExecutorService service = Executors.newFixedThreadPool(10 ); Future<Integer> future = service.submit(() -> { throw new IllegalArgumentException(); }); try { Thread.sleep(2000 ); System.out.println("线程是否执行完毕:" + future.isDone()); System.out.println("线程执行结果:" + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } service.shutdown(); } } `
cancel(boolean mayInterruptIfRunning)方法可以取消线程,分以下这三种情况:
如果线程没有开始:直接取消方法返回true。
如果线程已完成或者已取消:返回false。
如果线程已经开始执行:那么将会根据参数是否中断执行的线程。当mayInterruptIfRunning为false的时候,任务会继续执行。当mayInterruptIfRunning为true的时候,会给执行任务的线程一个中断的信号。
cancel实例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.concurrent.*;public class Cancel { public static void main (String[] args) throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(10 ); Future<Integer> future = service.submit(() -> { try { Thread.sleep(1000 ); } catch (InterruptedException e) { System.out.println("任务被中断了" ); } return 1 ; }); Thread.sleep(100 ); try { boolean cancel = future.cancel(true ); System.out.println("线程是否取消成功:" + cancel); System.out.println("线程执行结果:" + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (CancellationException e) { e.printStackTrace(); } service.shutdown(); } }
FutureTask FutureTask除了实现Future接口外,还实现了Runnable接口。因此,FutureTask可以交给Executor执行,也可以由Thread类直接执行。
FutureTask实例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class FutureTaskDemo { public static void main (String[] args) { FutureTask<Integer> futureTask = new FutureTask<>(() -> 1 ); Thread thread = new Thread(futureTask); thread.start(); try { System.out.println("获取线程结果:" + futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }