likes
comments
collection
share

多线程-有返回结果Callable

作者站长头像
站长
· 阅读数 18

背景

最简单的多线程是Runnable,最大的特点是:没有返回结果。

那怎么才能有返回结果?使用Callable。

官方文档-Callable


@FunctionalInterface
public interface Callable<V>

A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call. 返回结果并可能引发异常的任务。实现者定义一个不带参数的方法,称为 call 。

The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception. Callable 接口与 Runnable 类似,因为两者都是为实例可能由另一个线程执行的类而设计的。但是, Runnable 不会返回结果,也不能引发已检查的异常。

The Executors class contains utility methods to convert from other common forms to Callable classes. Executors 类包含从其他常见形式转换为 Callable 类的实用方法。

源码-Callable

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception; //和Runnable最大的区别就是有没有返回结果
}

和Runnable最大的区别,就是有返回数据。

Callable作用

Callable是Java中的一个接口,它位于java.util.concurrent包中。它的主要作用是允许你在多线程环境中执行可返回结果的任务,并能够获取任务执行的结果或处理任务执行过程中的异常。

主要特点和作用如下:

  1. 可返回结果:Runnable接口不同,Callable可以返回一个结果。这个结果的类型由泛型参数指定,例如Callable<Integer>可以返回一个整数结果。

  2. 异常处理: Callablecall方法可以抛出异常,你可以捕获这些异常并进行适当的处理。

  3. 多线程任务执行: 你可以将Callable任务提交给ExecutorService的线程池来执行,这允许你在多个线程中并行执行任务。

  4. 获取任务结果: 通过Future对象,你可以异步获取任务执行的结果。Future提供了方法来等待任务完成并获取其结果。

  5. 阻塞等待: 如果需要等待任务完成,可以使用Futureget方法,它会阻塞当前线程直到任务完成并返回结果。

下面是一个简单的示例,演示了如何使用CallableExecutorService来执行一个可返回结果的任务:

import java.util.concurrent.*;

public class CallableExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Callable<Integer> task = () -> {
            // 在这里执行耗时操作
            Thread.sleep(2000);
            return 42;
        };

        Future<Integer> future = executorService.submit(task);

        // 这里可以做一些其他的工作

        // 获取任务执行结果,如果任务还未完成,会阻塞当前线程直到完成
        int result = future.get();
        System.out.println("任务结果:" + result);

        executorService.shutdown();
    }
}

总的来说,Callable使多线程编程更加灵活,允许你执行可返回结果的任务,并能够处理异常和获取任务的执行结果。这对于需要并发处理任务的应用程序非常有用。

有了Runnable,为什么还要Callable?

虽然RunnableCallable都用于在多线程环境中执行任务,但它们之间存在一些重要的区别和应用场景,这就解释了为什么Java提供了这两个不同的接口。

下面是一些区别和理由为什么要使用Callable

  1. 返回结果:

    • Runnable不返回任何结果,它的run方法没有返回值。如果你需要任务执行后返回一个结果,你必须使用其他机制(如实例变量或共享队列)来获取结果。
    • Callable可以返回一个结果,这个结果的类型由泛型参数指定。这让任务的结果更容易访问和处理。
  2. 异常处理:

    • Runnablerun方法不能抛出受检查的异常(除非使用不推荐的Thread.setDefaultUncaughtExceptionHandler方法),因此它通常不适合处理可能会抛出异常的任务。
    • Callablecall方法可以抛出受检查的异常,这意味着你可以更容易地处理任务执行过程中的异常情况。
  3. Future对象:

    • Callable任务提交后,会返回一个Future对象,你可以使用这个对象来异步获取任务的结果。这对于需要等待任务完成的情况非常有用,你可以在需要时阻塞等待结果。
    • Runnable没有内置的机制来获取任务的执行结果,因此你需要自己实现一种方式来获取结果。
  4. 适用性:

    • Callable通常更适合需要执行、等待和处理任务结果的情况,特别是在并发编程和多线程环境中。
    • Runnable通常更适合那些不需要返回结果或不需要处理任务执行过程中的异常的简单任务。

综上所述,CallableRunnable都有其独特的用途和优势。选择哪个接口取决于你的具体需求,如果你需要任务返回结果、处理异常,并且更高级的任务控制,那么Callable可能更适合你。如果只是执行简单的任务而不需要返回结果或处理异常,那么Runnable可能足够了。

Callable比Runnable优点

Callable 相对于 Runnable 具有一些明显的优点,特别是在需要更丰富的多线程任务处理和结果获取方面。以下是一些 Callable 相对于 Runnable 的优点:

  1. 返回结果: 最大的优点之一是 Callable 允许任务返回一个结果。这意味着任务可以执行一些计算或操作,并将结果返回给调用者。这在处理需要计算或产生结果的并发任务时非常有用。

  2. 异常处理: Callablecall 方法可以抛出受检查的异常,因此它更适合处理可能会出现异常情况的任务。这使得你能够更好地处理任务执行过程中的错误情况。

  3. Future 对象: Callable 任务提交后,会返回一个 Future 对象,允许你异步获取任务的结果。这对于需要等待任务完成并在需要时获取结果的情况非常有用。你可以使用 Future 的方法来等待任务完成,检查任务是否已经完成,获取结果,或者取消任务的执行。

  4. 更丰富的任务控制: Callable 接口与 Future 结合使用,提供了更多任务控制的能力。你可以取消任务的执行,设定任务的超时时间,以及通过多个 Future 对象来协同执行多个任务。

  5. 泛型支持: Callable 使用泛型来指定返回结果的类型,这增加了类型安全性。你可以明确指定任务返回的数据类型,使代码更具可维护性和可读性。

总之,Callable 相对于 Runnable 提供了更多的功能和控制选项,使得多线程编程更加灵活和强大。然而,也要注意 Callable 的使用需要更多的代码和处理,所以在简单任务的情况下,选择适用的接口是有必要的。