Callable接口、Runable接口、FutureTask接口

Bertha 。 2022-02-25 00:54 734阅读 0赞

1、Callable与Runable区别

  1. Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理。**Thread**类、**Runnable**接口和Java内存管理模型使得多线程编程简单直接。
  2. Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。没有返回值这点稍微有点麻烦。不能声明抛出检查型异常则更麻烦一些。

public void run()方法契约意味着你必须捕获并处理检查型异常。即使你小心地保存了异常信息(在捕获异常时)以便稍后检查,但也不能保证这个类(Runnable对象)的所有使用者都读取异常信息。

  1. 你也可以修改Runnable实现的getter,让它们都能抛出任务执行中的异常。但这种方法除了繁琐也不是十分安全可靠,你不能强迫使用者调用这些方法,程序员很可能会调用join()方法等待线程结束然后就不管了。
  2. 但是现在不用担心了,以上的问题终于在1.5中解决了。Callable接口和Future接口的引入以及他们对线程池的支持优雅地解决了这两个问题。
  3. 不管用哪种方式创建线程,其本质都是Callable接口与Runable接口。两者都是可被其它线程执行的任务!!区别是:
  4. 1Callable规定的方法是call(),而Runnable规定的方法是run()。
  5. 2Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
  6. 3call()方法可抛出异常,而run()方法是不能抛出异常的。
  7. 4)运行Callable任务可拿到一个Future对象。

2、Future

如上所说,Callable任务返回Future对象。即:Callable和Future一个产生结果,一个拿到结果。

Future 表示异步计算的结果。Future接口中有如下方法:

  • boolean cancel (boolean mayInterruptIfRunning):试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程,true中断,false不中断。此方法返回后,对 isDone() 的后续调用将始终返回 true。
  • boolean isCancelled():如果在任务正常完成前将其取消,则返回 true。
  • boolean isDone():如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。
  • V get()throws InterruptedException,ExecutionException:如有必要,等待计算完成,然后获取其结果,阻塞
  • V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。

3、FutureTask

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70

FutureTask将Callable封装成Rannable。

我们先来看一下FutureTask的实现:

  1. public class FutureTask<V> implements RunnableFuture<V>
  2. FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
  3. public interface RunnableFuture<V> extends Runnable, Future<V> {
  4. void run();
  5. }

Future是一个接口, **FutureTask类是Future 的一个实现类,并实现了Runnable,因此FutureTask可以传递到线程对象Thread中新建一个线程执行。所以可通过Excutor(线程池) 来执行,也可传递给Thread对象执行。**如果在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。

  FutureTask是为了弥补Thread的不足而设计的,它可以让程序员准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果(如果有需要)。

  1. FutureTask**是一种可以取消的异步的计算任务**。它的计算是通过Callable实现的,它等价于可以携带结果的Runnable,并且有三个状态:等待、运行和完成。完成包括所有计算以任意的方式结束,包括正常结束、取消和异常。
  2. Executor框架利用FutureTask来完成异步任务,并可以用来进行任何潜在的耗时的计算。**一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。**

4、使用示例

1. 使用Callable+Future获取执行结果:

  1. public class Test {
  2. public static void main(String[] args) {
  3. ExecutorService executor = Executors.newCachedThreadPool();
  4. Task task = new Task();
  5. Future<Integer> result = executor.submit(task);
  6. executor.shutdown();
  7. try {
  8. Thread.sleep(1000);
  9. } catch (InterruptedException e1) {
  10. e1.printStackTrace();
  11. }
  12. System.out.println("主线程在执行任务");
  13. try {
  14. System.out.println("task运行结果"+result.get());
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. } catch (ExecutionException e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("所有任务执行完毕");
  21. }
  22. }
  23. class Task implements Callable<Integer>{
  24. @Override
  25. public Integer call() throws Exception {
  26. System.out.println("子线程在进行计算");
  27. Thread.sleep(3000);
  28. int sum = 0;
  29. for(int i=0;i<100;i++)
  30. sum += i;
  31. return sum;
  32. }
  33. } 执行结果:
  34. 子线程在进行计算
  35. 主线程在执行任务
  36. task运行结果4950
  37. 所有任务执行完毕

2.使用Callable+FutureTask获取执行结果

  1. public class Test {
  2. public static void main(String[] args) {
  3. //第一种方式
  4. ExecutorService executor = Executors.newCachedThreadPool();
  5. Task task = new Task();
  6. FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
  7. executor.submit(futureTask);
  8. executor.shutdown();
  9. //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
  10. /*Task task = new Task();
  11. FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
  12. Thread thread = new Thread(futureTask);
  13. thread.start();*/
  14. try {
  15. Thread.sleep(1000);
  16. } catch (InterruptedException e1) {
  17. e1.printStackTrace();
  18. }
  19. System.out.println("主线程在执行任务");
  20. try {
  21. System.out.println("task运行结果"+futureTask.get());
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. } catch (ExecutionException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("所有任务执行完毕");
  28. }
  29. }
  30. class Task implements Callable<Integer>{
  31. @Override
  32. public Integer call() throws Exception {
  33. System.out.println("子线程在进行计算");
  34. Thread.sleep(3000);
  35. int sum = 0;
  36. for(int i=0;i<100;i++)
  37. sum += i;
  38. return sum;
  39. }
  40. }

转载:

http://www.cnblogs.com/dolphin0520/

发表评论

表情:
评论列表 (有 0 条评论,734人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Callable&Future 接口

    Callable接口 创建线程的方法 一种是通过创建Thread类,另一种是通过使用Runnable创建线程。但是,Runnable缺少的一项功能是,当线程终止时(

    相关 Callable接口和Future

    本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。 Callable接口类似于Runnable,从名字就可以看出来了,但是Runnab