• 多线程(四) | 聊聊Callable和Future


    hello, 大家好,又见面了。

    通过前面几篇文章,我相信大家应该对于如何使用多线程执行任务应该都有了一定的了解。今天我们来讲一讲Callable和Future。

    我们之前的线程任务都是使用的Runnable接口,这个任务最终的体现是写在里面 run方法中的内容,大家要这个run() 方法是没有返回值的,也就代表这我们使用Runnable 作为线程任务的时候是没有返回值的。

    那比如我又一个需求,需要开启一个新的线程去统计一个结果,那这个线程执行结束之后,是需要把统计出来的结果告诉我的,很遗憾, Runnable是做不到的,他做多能帮我们打印到控制台上,但是并没有什么用。这也就引出了Callable的作用:

    Callable: 也是一个接口,用来描述线程任务,但是他是一个有返回值的线程任务。

    一、基本用法

    所以以后我们在开发线程任务的时候就用两种选择,一种是Runnable代表没有返回值的线程任务;一种是Callable代表有返回值的线程任务。

    同时Callable是可以抛出异常的,而Runnable无法抛出,只能在方法中通过try..catch处理。

    那我们先来了解一下Callable的用法。callable接口中只有一个方法,叫做call, 它有返回值。这个方法适合Runnable中的run方法想对应的。

    1. @FunctionalInterface
    2. public interface Callable {
    3. /**
    4. * Computes a result, or throws an exception if unable to do so.
    5. *
    6. * @return computed result
    7. * @throws Exception if unable to compute a result
    8. */
    9. V call() throws Exception;
    10. }

    那么我们应该如何执行Callable呢?看一段代码:

    1. package com.lsqingfeng.action.knowledge.multithread.callable;
    2. import java.util.concurrent.Callable;
    3. import java.util.concurrent.ExecutionException;
    4. import java.util.concurrent.FutureTask;
    5. import java.util.concurrent.TimeUnit;
    6. /**
    7. * @className: CallableDemo
    8. * @description: Callable案例
    9. * @author: sh.Liu
    10. * @date: 2022-04-08 15:03
    11. */
    12. public class CallableDemo {
    13. public static void main(String[] args) throws ExecutionException, InterruptedException {
    14. // 1. 创建一个有返回值的线程任务Callable, 返回String
    15. Callable c = new Callable(){
    16. @Override
    17. public Object call() throws Exception {
    18. TimeUnit.SECONDS.sleep(2);
    19. System.out.println(Thread.currentThread().getName() + "| 正在执行有返回值任务");
    20. return "success";
    21. }
    22. };
    23. // 2. 创建一个Future用来执行多线程任务,构造方法中传入Callable类型变量
    24. FutureTask futureTask = new FutureTask<>(c);
    25. // 创建Thread 线程,传入futureTask
    26. Thread t1 = new Thread(futureTask);
    27. // 3. 使用futureTask 执行线程任务
    28. t1.start();
    29. // 4. 获取该任务的返回结果:
    30. System.out.println(futureTask.get());
    31. }
    32. }

    打印结果:

    Thread-0| 正在执行有返回值任务 success

    执行结果在两秒后打印出来,也证明了 get方法是个阻塞的方法。

    二、线程池执行Callable

    除了上面的方式以外,我们仍然可以使用线程池来运行有返回值的线程任务-Callable。

    1. package com.lsqingfeng.action.knowledge.multithread.callable;
    2. import java.util.concurrent.*;
    3. /**
    4. * @className: CallableDemo2
    5. * @description: Callable案例: 使用线程池执行Callable
    6. * @author: sh.Liu
    7. * @date: 2022-04-08 16:35
    8. */
    9. public class CallableDemo2 {
    10. public static void main(String[] args) throws ExecutionException, InterruptedException {
    11. // 1. 创建一个线程池
    12. ExecutorService executorService = Executors.newFixedThreadPool(2);
    13. // 2. 创建一个Callable 线程任务
    14. Callable c1 = ()->{
    15. TimeUnit.SECONDS.sleep(2);
    16. System.out.println(Thread.currentThread().getName() + "| 开始执行任务----");
    17. return 1;
    18. };
    19. // 3. 执行线程任务,获取结果
    20. Future future = executorService.submit(c1);
    21. // 4.打印结果
    22. System.out.println(future.get());
    23. }
    24. }

    三、给Runnable添加返回值

    如果说我们现在有一个Runnable类型的任务,也想在执行完任务的时候,给调用者返回一个结果。这个时候怎么办呢?Java中也未我们提供了相关的API:

    这个问题归根接地是怎么把一个Runnable转为Callable的问题。

    1. package com.lsqingfeng.action.knowledge.multithread.callable;
    2. import java.util.concurrent.*;
    3. /**
    4. * @className: CallableDemo3
    5. * @description:
    6. * @author: sh.Liu
    7. * @date: 2022-04-08 17:05
    8. */
    9. public class CallableDemo3 {
    10. public static void main(String[] args) {
    11. Runnable r1 = ()->{
    12. System.out.println("I am Runnable");
    13. };
    14. // 转换成Callable:
    15. Callable c1 = Executors.callable(r1, "result");
    16. // 然后运行Callable的方式即可:
    17. // 方式二: 使用FutureTask的方式,传入Runnable和结果
    18. FutureTask futureTask = new FutureTask(r1, "result2");
    19. new Thread((futureTask)).start();
    20. // 方式三: 使用线程池, 提交的时候传入Runnable和result
    21. ExecutorService executorService = Executors.newFixedThreadPool(3);
    22. Future result2 = executorService.submit(r1, "result2");
    23. }
    24. }

    好了,关于Callable的内容我们就介绍到这里了。整体来说用法还是比较简单的。就记住有返回值的多线程就是用Callable。如果对你有帮助,记得点点关注。下篇文章我们聊点别的。

  • 相关阅读:
    Apollo 应用与源码分析:CyberRT-话题通信
    人工智能基础_机器学习001_线性回归_多元线性回归_最优解_基本概念_有监督机器学习_jupyter notebook---人工智能工作笔记0040
    新入手Mac配置前端环境教程
    工业智能化转型升级难?华为云这三招,加速商业变现
    JavaScript系列从入门到精通系列第六篇:JavaScript中其他进制数字以及其他的数据类型强制转换为Boolean数据类型
    嵌入式Python高级
    [C++基础]-类和对象(中~万字详解)
    单机高并发模型设计
    Unity List相关问题
    【MySQL】基本查询(表的增删改查)-- 详解
  • 原文地址:https://blog.csdn.net/lsqingfeng/article/details/127492867