通过 Callable 和 Future(或 FutureTask)创建线程

在Java中,可以使用Callable和Future创建线程。Callable接口类似于Runnable接口,但是它可以返回一个结果并且可以抛出异常。Future则是用于表示异步计算的结果,可以等待计算完成并且获取结果。

Callable 和 Future 的优点

使用Callable和Future创建线程相较于直接使用Runnable接口或Thread类有一些优点和好处:

  1. 能够返回结果:Callable接口可以返回计算的结果,这在某些场景下非常有用。例如,如果你需要执行一个耗时的计算任务,并且需要获取计算的结果进行后续处理,那么Callable就可以很方便地实现这个需求。
  2. 能够抛出异常 :Callable接口的call()方法可以声明抛出异常,这使得在执行过程中出现异常时能够更好地处理错误情况,而不是直接使线程崩溃。
  3. 更灵活的线程池管理:ExecutorService接口提供了灵活的线程池管理,可以方便地控制并发线程数量、提交任务、获取任务执行结果等。使用Callable和Future结合ExecutorService可以更加方便地管理并发任务。
  4. 阻塞等待任务完成 :Future对象的get()方法可以阻塞当前线程,直到任务执行完成并返回结果。这在需要等待任务执行完成后再继续执行后续操作时非常有用。
  5. 支持取消任务 :Future对象提供了cancel()方法,可以取消任务的执行。这在某些情况下可以帮助避免资源浪费或处理超时任务。

总的来说,使用Callable和Future创建线程能够提供更灵活、更可控的并发执行方式,使得多线程编程更加方便、高效、安全。

Callable 和 Future 的使用步骤

  1. 创建Callable任务 :首先,你需要创建一个实现了Callable接口的任务类,该接口要求实现call()方法,该方法表示需要执行的具体任务,并且可以返回一个结果。
  2. 创建ExecutorService:接下来,你需要创建一个ExecutorService对象,它用于管理线程池和执行任务。
  3. 提交任务 :使用ExecutorService的submit()方法提交Callable任务。这个方法会返回一个Future对象,用于表示任务的执行情况和结果。
  4. 获取Future对象 :通过调用submit()方法后返回的Future对象,你可以对任务进行管理,例如等待任务执行完成或者取消任务。
  5. 获取结果 :如果你需要获取任务执行的结果,可以调用Future对象的get()方法。这个方法会阻塞当前线程,直到任务执行完成并返回结果。
  6. 关闭ExecutorService :当所有任务执行完成后,记得调用ExecutorService的shutdown()方法关闭线程池,释放资源。

Callable 和 Future 的代码样例

下面是一个简单的示例,演示了如何使用Callable和Future创建线程:

java 复制代码
import java.util.concurrent.*;

public class CallableAndFutureDemo {

    public static void main(String[] args) {
        // 创建一个线程
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 创建一个 Callable 任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 模拟一个耗时操作
                Thread.sleep(2000);
                return 123;
            }
        };

        // 提交任务并获取 Future 对象
        Future<Integer> future = executor.submit(callable);

        // 等待计算完成并获取结果
        try {
            // 这一步会阻塞,直到任务完成
            Integer result = future.get();
            System.out.println("result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // 关闭线程池
        executor.shutdown();
    }

}

这个示例中,我们创建了一个Callable任务,并通过ExecutorService的submit()方法提交该任务。然后,我们通过Future对象的get()方法等待任务执行完成并获取结果。最后,我们调用ExecutorService的shutdown()方法关闭线程池。

FutureTask 的优点

当使用Callable和Future时,经常会使用到FutureTask类。FutureTask是Future接口的实现类,同时也实现了Runnable接口,因此它可以被提交到ExecutorService中执行,并且可以作为Future对象来获取任务的执行结果。

以下是FutureTask的优点和使用方法:

  1. 方便的结合Callable和Runnable:FutureTask既可以包装Callable任务,也可以直接包装Runnable任务,这使得使用FutureTask非常灵活,既可以获取任务执行结果,又可以在ExecutorService中执行。
  2. 简化线程管理:FutureTask提供了一种方便的方式来管理异步任务。通过将任务包装在FutureTask中,可以更容易地提交、取消、等待和获取任务的执行结果。
  3. 异常处理 :FutureTask能够处理Callable任务中抛出的异常。如果任务执行过程中出现异常,调用FutureTask的get()方法会抛出ExecutionException,并将原始异常作为其cause。
  4. 支持取消任务 :FutureTask提供了cancel()方法来取消任务的执行。可以选择传入一个布尔值参数来指定是否中断正在执行的任务。
  5. 支持多线程操作:FutureTask是线程安全的,可以在多个线程中安全地使用。

FutureTask 的示例代码1

使用FutureTask的示例代码如下:

java 复制代码
import java.util.concurrent.*;

public class CallableAndFutureTaskDemo {

    public static void main(String[] args) {

        // 创建一个 Callable 任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 模拟一个耗时操作
                Thread.sleep(2000);
                return 123;
            }
        };

        // 提交任务并获取 Future 对象
        FutureTask<Integer> future = new FutureTask<>(callable);

        // 创建一个线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交 FutureTask 任务
        executor.submit(callable);

        // 获取任务执行结果
        try {
            // 这一步会阻塞,直到任务完成
            Integer result = future.get();
            System.out.println("result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // 关闭线程池
        executor.shutdown();
    }

}

在这个示例中,我们创建了一个Callable任务,并使用FutureTask包装了它。然后,我们将FutureTask提交到ExecutorService中执行,并通过调用get()方法获取任务的执行结果。最后,我们关闭了ExecutorService。

FutureTask 的示例代码2

使用 Callable, FutureTask 和 Thread,而不是线程池来创建线程,代码示例如下:

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableAndFutureTaskDemo1 {

    public static void main(String[] args) {
        
        // 创建一个Callable任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 模拟一个耗时操作
                Thread.sleep(2000);
                return 123;
            }
        };

        // 创建FutureTask对象
        FutureTask<Integer> futureTask = new FutureTask<>(callable);

        // 创建一个线程,并将FutureTask作为其构造函数参数
        Thread thread = new Thread(futureTask);

        // 启动线程
        thread.start();

        // 获取任务执行结果
        try {
            Integer result = futureTask.get(); // 这一步会阻塞直到任务完成
            System.out.println("Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

在这个示例中,我们创建了一个Callable任务,并使用FutureTask包装了它。然后,我们创建了一个Thread对象,将FutureTask作为其构造函数参数传入,这样Thread就知道要执行的任务。接着,我们启动了线程并等待任务执行完成,最后获取任务的执行结果。

相关推荐
风铃儿~14 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组20 分钟前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿30 分钟前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA43 分钟前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
gaoliheng0061 小时前
Redis看门狗机制
java·数据库·redis
我是唐青枫1 小时前
.NET AOT 详解
java·服务器·.net
Su米苏1 小时前
Axios请求超时重发机制
java
Undoom2 小时前
🔥支付宝百宝箱新体验!途韵归旅小帮手,让高铁归途变旅行
后端
不超限2 小时前
Asp.net Core 通过依赖注入的方式获取用户
后端·asp.net