• Java【多线程】Callable 是什么, 如何使用并理解 Cllable, 和 Runnable 有什么区别?



    前言

    📕各位读者好, 我是小陈, 这是我的个人主页
    📗小陈还在持续努力学习编程, 努力通过博客输出所学知识
    📘如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽
    📙 希望我的专栏能够帮助到你:
    JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
    Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
    JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

    这篇文章 介绍了线程的概念和线程的创建方式(继承 Thread 类或实现 Runnable 接口), 本篇介绍另一种创建线程的方式 : 还可以实现 Cllable 接口


    提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎批评指点~ 废话不多说,直接上干货!

    一、Callable 是什么?

    Callable 是一个函数式接口, 也是用于创建线程, 把线程封装了一个 “返回值”, 方便程序猿借助多线程的方式计算结果

    无论是继承 Thread 类还是实现 Runnable 接口, 都是重写 run() 方法来描述一个线程执行的工作, 形如 :

        @Override
        public void run() {
            System.out.println("这里是线程运行的代码");
       }
    
    • 1
    • 2
    • 3
    • 4

    可以看到 run() 方法是没有返回值的


    二、不使用 Callable 如何创建“有返回值的”线程?

    例如我们要计算 0~100 累加的和

    • 创建一个类 Result , 包含一个 sum 表示最终结果, 以及一个 lock 表示线程同步使用的锁对象
    • main 方法中先创建 Result 实例, 然后创建一个线程 t. 在线程内部计算 1 + 2 + 3 + … + 100
    • 主线程同时使用 wait() 等待线程 t 计算结束
    • 当线程 t 计算完毕后, 通过 notify() 唤醒主线程, 主线程再打印结果
    static class Result {
        public int sum = 0;
        public Object lock = new Object();
    }
    
    public static void main(String[] args) throws InterruptedException {
        Result result = new Result();
        Thread t = new Thread() {
            @Override
            public void run() {
                int sum = 0;
                for (int i = 1; i <= 1000; i++) {
                    sum += i;
               }
                synchronized (result.lock) {
                    result.sum = sum;
                    result.lock.notify();
               }
           }
       };
        t.start();
        synchronized (result.lock) {
            while (result.sum == 0) {
            	// 等待 t 线程计算完之后唤醒当前阻塞状态
                result.lock.wait();
           }
            System.out.println(result.sum);
       }
    }
    
    • 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

    虽然这种方式也获取到了线程执行完毕的结果, 但本质上并不是作为返回值的, 而是两个线程修改了同一个变量, 这个变量是 result 类的成员属性, 并且实现的过程比较复杂

    所以如果需要获取一个线程执行完毕的结果, 使用 Callable 更方便不易出错


    三、如何使用 Callable?

    还是计算 0~100 累加的和

    • 创建一个匿名内部类, 实现 Callable 接口, Callable 带有泛型参数, 泛型参数表示返回值的类型
    • 重写 Callable 的 call() 方法, 描述线程执行的工作, 通过返回值返回计算结果
    • 创建 FutureTask 类, 构造方法的参数是 callable 实例
    • 创建 Thread 类(线程), 构造方法的参数是 futureTask 实例, 此时新线程就会执行 FutureTask 内部的 Callable 的 call() 方法, 完成计算, 计算结果就放到了 FutureTask 对象中
    • 在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结果
    Callable<Integer> callable = new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            int sum = 0;
            for (int i = 1; i <= 1000; i++) {
                sum += i;
            }
            return sum;
       }
    };
    FutureTask<Integer> futureTask = new FutureTask<>(callable);
    Thread t = new Thread(futureTask);
    t.start();
    int result = futureTask.get();
    System.out.println(result);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Callable 和 Runnable 同样是函数式接口, 所以也支持 lambda 表达式的写法

    FutureTask<Integer> futureTask = new FutureTask<>( ()->{
    	int sum = 0;
    	for (int i = 1; i <= 1000; i++) {
    		sum += i;
    	}
    	return sum;
    });
    Thread t = new Thread(futureTask);
    t.start();
    int result = futureTask.get();
    System.out.println(result);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、如何理解 Callable?

    在这里插入图片描述

    • Callable 是一个接口, 需要重写 call() 方法, 这个方法相当于 Thread 和 Runnable 的 run(), 都是用来描述一个线程要执行的工作, call() 有返回值, run() 没有返回值
    • FutureTask 只是作为一层包装, 就相当于获取返回值的 “凭证”

    总结

    以上就是本篇的全部内容, 主要介绍了使用 Callable 接口来创建有返回值的线程

    基本步骤 :

    • 创建一个类, 实现 Callable 接口
    • 重写 Callable 的 call() 方法
    • 创建 FutureTask 类
    • 创建 Thread 类
    • 调用 futureTask.get()

    如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~


    上山总比下山辛苦
    下篇文章见

    在这里插入图片描述

  • 相关阅读:
    【毕业设计】深度学习车辆颜色识别检测系统 - python opencv YOLOv5
    【图像融合】基于matlab DSIFT多聚焦图像融合【含Matlab源码 2224期】
    全球名校AI课程库(38)| 马萨诸塞大学 · 自然语言处理进阶课程『Advanced Natural Language Processing』
    mybatis plus自定义模板
    Linux-Hadoop集群配置
    AD7606模块
    【编程题】【Scratch一级】2022.06 报时的公鸡
    Blazor实战——Known框架多表增删改查
    H3C S7000/S7500E/10500系列交换机Console密码忘记处理方法
    想要提高客户留资率?一个留资机器人就够了!
  • 原文地址:https://blog.csdn.net/yzhcjl_/article/details/133415869