通过 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就知道要执行的任务。接着,我们启动了线程并等待任务执行完成,最后获取任务的执行结果。

相关推荐
魔道不误砍柴功13 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_23413 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨16 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java