Callable

Callable

Callable接口的定义:

  • 泛型类型 V:指定了 call() 方法返回值的类型。Callablecall() 方法会返回一个类型为 V 的值。
  • call() 方法 :与 Runnablerun() 方法类似,call() 方法包含了任务的执行逻辑。不同的是,call() 方法可以返回一个结果 (通过返回值 V),并且它可以抛出异常

Runnable 的对比

特性 Runnable Callable
返回值 run() 方法没有返回值 call() 方法有返回值
异常处理 run() 方法不能抛出任何异常 call() 方法可以抛出异常
使用场景 用于不需要返回值或异常处理的场景 用于需要返回值或可能抛出异常的场景

Callable 的工作原理

Callable 的原理是基于 线程池任务提交机制 ,尤其是 ExecutorService,可以通过 ExecutorService.submit() 方法提交任务,并获取 Future 对象。Callable 的执行过程大致如下:

  1. 任务提交 :将 Callable 任务提交给一个线程池(例如,ExecutorService)。线程池会创建一个新的线程(或复用现有的空闲线程)来执行该任务。
  2. 任务执行 :线程池中的线程会调用 Callable.call() 方法执行任务的具体操作。
  3. 返回结果 :任务执行完成后,call() 方法会返回一个结果值(类型为 V)。如果 call() 方法抛出了异常,异常会被捕获并封装在 Future
  4. 获取结果 :使用 Future.get() 方法可以获取 Callable 执行的返回值。Future.get() 是一个阻塞方法,直到任务执行完毕并返回结果。

CallableFuture 的配合

java 复制代码
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<>(new MyThread());
        new Thread(futureTask).start();
        Object o = futureTask.get();
        System.out.println(o);
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println("callable");
        return 123;
    }
}

如何在Thread中使用Callable

这就需要callableFuture的配合使用了,我们查看源码可以看到

Callable原本与Thread没有联系的,是通过RunnableFuture接口建立了联系;

为了使 Callable 可以与 Thread 配合使用,java.util.concurrent 包中引入了 RunnableFuture 接口,它继承了 RunnableFuture 接口:

  • Runnable 接口使得 RunnableFuture 可以在 Thread 中被执行。
  • Future 接口提供了获取结果和取消任务的能力。

于是在new Thread()里边就可以使用Callable

FutureTask 实现

RunnableFuture 接口的实现类是 FutureTask,它实现了 CallableRunnableFuture,这就是 CallableThread 之间的桥梁。FutureTask 可以包装一个 Callable 实例并通过 run() 方法在 Thread 中执行。

但是有两个问题

1.Future.get()

异常处理

  • 如果 Callable 中的 call() 方法抛出了异常,Future.get()包装 这个异常并重新抛出。
  • 异常类型为 ExecutionException,可以通过 getCause() 获取原始的异常。

不过这个 get是阻塞的,需要等待获取到里边的内容才会往下继续进行;所以一般会将它放到代码的最后一步执行,防止阻塞线程;或是异步处理;

2.结果会被缓存

java 复制代码
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<>(new MyThread());//适配类
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();
        Object o = futureTask.get();
        System.out.println(o);
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println("callable");
        return 123;
    }
}

在这个代码中 你觉得"callable" 会打印几次?

结果只是打印了一次,因为FutureTask 有一个非常重要的特性:它缓存任务的执行结果。即使同一个 FutureTask 被多个线程共享执行,它的结果也 只会被计算一次 ,之后所有的线程都会获得相同的结果。

通过源码可以得知,是否已经被缓存了。

相关推荐
朦胧之1 小时前
PostgreSQL 数据库笔记
人工智能·后端
IT_陈寒3 小时前
Redis缓存雪崩把我坑惨了,这次长记性了
前端·人工智能·后端
LucianaiB4 小时前
用 WorkBuddy 研究腾讯、阿里和 DeepSeek,我没敢直接说「AI 很赚钱」
后端
用户8356290780514 小时前
使用 Python 管理 PDF 属性和元数据
后端·python
分支预测失败4 小时前
RISC-V 上下文切换实战:从 Trap 入口到 Linux 进程调度
后端·嵌入式
用户8356290780515 小时前
使用 Python 在 Word 文档中创建自定义图表
后端·python
Profile排查笔记5 小时前
JavaScript 实现浏览器指纹生成:基础字段、Canvas 采样与 SHA-256 摘要
前端·人工智能·后端·自动化
GoGeekBaird5 小时前
我给 DeepSeek Harness 造了个铸造台
后端
YOU OU5 小时前
Spring Cloud概述
后端·spring·spring cloud
半个落月5 小时前
React JWT 登录鉴权实战:Mock、Zustand、Axios 与路由守卫全流程
后端·react.js