• java线程——详解Callable、Future和FutureTask


    回顾:


    接上篇博客

    java线程——三种创建线程的方式 ,这篇博客主要介绍第三种方式Callable和Future。比较继承Thread类和实现Runnable接口,接口更加灵活,使用更广泛。但这两种方式都没有返回值,要想返回相应的数据,就要使用Callable和Future方式。


    基础:

    1、Callable

    还是从定义开始,Callable接口有返回值,并且可以抛出异常。

    1. /**(有返回值的任务,可能抛出异常)
    2. * A task that returns a result and may throw an exception.
    3. * Implementors define a single method with no arguments called
    4. * {@code call}.
    5. * @see Executor
    6. * @since 1.5
    7. * @author Doug Lea
    8. * @param <V> the result type of method {@code call}
    9. */
    10. @FunctionalInterface
    11. public interface Callable<V> {
    12. V call() throws Exception;
    13. }


    2、Future

    Future同样也是一个接口,主要方法如下,方法的功能比较容易理解,所以就没有写注释。主要作用:获取任务执行结果,中断任务等。

    1. package java.util.concurrent;
    2. public interface Future<V> {
    3. boolean cancel(boolean mayInterruptIfRunning);
    4. boolean isCancelled();
    5. boolean isDone();
    6. V get() throws InterruptedException, ExecutionException;
    7. V get(long timeout, TimeUnit unit)
    8. throws InterruptedException, ExecutionException, TimeoutException;
    9. }

    3、FutureTask

    1. public class FutureTask<V> implements RunnableFuture<V> {
    2. ......
    3. }
    4. public interface RunnableFuture<V> extends Runnable, Future<V> {
    5. ......
    6. }


           FutureTask实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable和Future。也就是说,它既可以作为Runnable被线程执行,也可以作为Future得到Callable返回值。


    使用:

    方法一:Callable+Future

    1. public class CallableAndFuture {
    2. /**
    3. * 实现Callable接口
    4. *
    5. * @author YANG
    6. *
    7. */
    8. public static class MyCallable implements Callable {
    9. private int flag = 0;
    10. public MyCallable(int flag) {
    11. this.flag = flag;
    12. }
    13. // 重写call方法
    14. public String call() throws Exception {
    15. // 情况一:flag=0 返回0
    16. if (this.flag == 0) {
    17. return "flag = 0";
    18. }
    19. // 情况二:flag=1 返回looping 陷入死循环
    20. if (this.flag == 1) {
    21. try {
    22. while (true) {
    23. System.out.println("looping.");
    24. Thread.sleep(2000);
    25. }
    26. // 情况三:出现异常
    27. } catch (InterruptedException e) {
    28. System.out.println("Interrupted");
    29. }
    30. return "false";
    31. } else {
    32. throw new Exception("Bad flag value!");
    33. }
    34. }
    35. }
    36. public static void main(String[] args) {
    37. // 定义3个Callable类型的任务,构造方法中制定flag的值
    38. MyCallable task1 = new MyCallable(0);
    39. MyCallable task2 = new MyCallable(1);
    40. MyCallable task3 = new MyCallable(2);
    41. // 创建一个执行任务的服务
    42. ExecutorService es = Executors.newFixedThreadPool(3);
    43. try {
    44. // 提交并执行任务,任务启动时返回了一个Future对象,
    45. // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
    46. Future future1 = null;
    47. future1 = es.submit(task1);
    48. // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
    49. System.out.println("task1: " + future1.get());
    50. Future future2 = es.submit(task2);
    51. // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环
    52. Thread.sleep(5000);
    53. System.out.println("task2 cancel: " + future2.cancel(true));
    54. // 测试抛出异常
    55. Future future3 = es.submit(task3);
    56. System.out.println("task3: " + future3.get());
    57. } catch (Exception e) {
    58. System.out.println(e.toString());
    59. }
    60. // 停止任务执行服务
    61. es.shutdownNow();
    62. }
    63. }


    执行结果:




    方法二:Callable+FutureTask

          分析两种方法不同之处就在于Future和FutureTask,其中一个是接口,一个是类。因此,只有main方法调用部分不同,上面的MyCallable类中的内容保持不变。

    1. public static void main(String[] args) {
    2. MyCallable task1 = new MyCallable(0);
    3. FutureTask ft1 = new FutureTask(task1);
    4. MyCallable task2 = new MyCallable(1);
    5. FutureTask ft2 = new FutureTask(task2);
    6. MyCallable task3 = new MyCallable(2);
    7. FutureTask ft3 = new FutureTask(task3);
    8. try {
    9. //启动task1
    10. new Thread(ft1, "子线程").start();
    11. System.out.println(ft1.get());
    12. //等待5秒后,停止task2
    13. new Thread(ft2, "子线程").start();
    14. Thread.sleep(5000);
    15. System.out.println("task2 cancel:" + ft2.cancel(true));
    16. //启动task3
    17. new Thread(ft3, "子线程").start();
    18. System.out.println("task3:" + ft3.get());
    19. } catch (InterruptedException | ExecutionException e) {
    20. System.out.println(e.toString());
    21. }
    22. }

           

           其执行结果与方法一完全相同,对比这两种方式,第二种比较容易读懂,第一种相对困难些。下篇博客我们介绍Executor、ExecutorService等内容,相信之后理解起来就会很轻松了。

  • 相关阅读:
    基础算法:求目标值 &字符串反转
    算法计算出股票最佳交易时间点
    Python 设计模式—命令模式
    Python 设计模式—代理模式
    有趣的算法题~单调栈
    令人头大的字符串—算法处理
    WebUI 自动化测试的经典设计模式:PO
    双指针—滑动窗口算法解析
    Python 设计模式—观察者模式
    多线程之读写锁原理
  • 原文地址:https://www.cnblogs.com/jpfss/p/9329954.html
Copyright © 2020-2023  润新知