前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程(四) | 聊聊Callable和Future

多线程(四) | 聊聊Callable和Future

作者头像
一缕82年的清风
发布2022-11-02 18:26:15
2940
发布2022-11-02 18:26:15
举报
文章被收录于专栏:lsqingfenglsqingfeng

hello, 大家好,又见面了。

通过前面几篇文章,我相信大家应该对于如何使用多线程执行任务应该都有了一定的了解。今天我们来讲一讲Callable和Future。

我们之前的线程任务都是使用的Runnable接口,这个任务最终的体现是写在里面 run方法中的内容,大家要这个run() 方法是没有返回值的,也就代表这我们使用Runnable 作为线程任务的时候是没有返回值的。

那比如我又一个需求,需要开启一个新的线程去统计一个结果,那这个线程执行结束之后,是需要把统计出来的结果告诉我的,很遗憾, Runnable是做不到的,他做多能帮我们打印到控制台上,但是并没有什么用。这也就引出了Callable的作用:

Callable: 也是一个接口,用来描述线程任务,但是他是一个有返回值的线程任务。

一、基本用法

所以以后我们在开发线程任务的时候就用两种选择,一种是Runnable代表没有返回值的线程任务;一种是Callable代表有返回值的线程任务。

同时Callable是可以抛出异常的,而Runnable无法抛出,只能在方法中通过try..catch处理。

那我们先来了解一下Callable的用法。callable接口中只有一个方法,叫做call, 它有返回值。这个方法适合Runnable中的run方法想对应的。

代码语言:javascript
复制
@FunctionalInterface
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;
}

那么我们应该如何执行Callable呢?看一段代码:

代码语言:javascript
复制
package com.lsqingfeng.action.knowledge.multithread.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * @className: CallableDemo
 * @description: Callable案例
 * @author: sh.Liu
 * @date: 2022-04-08 15:03
 */
public class CallableDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. 创建一个有返回值的线程任务Callable, 返回String
        Callable<String> c = new Callable(){
            @Override
            public Object call() throws Exception {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "| 正在执行有返回值任务");
                return "success";
            }
        };

        // 2. 创建一个Future用来执行多线程任务,构造方法中传入Callable类型变量
        FutureTask<String> futureTask = new FutureTask<>(c);
        // 创建Thread 线程,传入futureTask
        Thread t1 = new Thread(futureTask);

        // 3. 使用futureTask 执行线程任务
        t1.start();

        // 4. 获取该任务的返回结果:
        System.out.println(futureTask.get());
    }
}

打印结果:

Thread-0| 正在执行有返回值任务 success

执行结果在两秒后打印出来,也证明了 get方法是个阻塞的方法。

二、线程池执行Callable

除了上面的方式以外,我们仍然可以使用线程池来运行有返回值的线程任务-Callable。

代码语言:javascript
复制
package com.lsqingfeng.action.knowledge.multithread.callable;

import java.util.concurrent.*;

/**
 * @className: CallableDemo2
 * @description: Callable案例: 使用线程池执行Callable
 * @author: sh.Liu
 * @date: 2022-04-08 16:35
 */
public class CallableDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. 创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 2. 创建一个Callable 线程任务
        Callable<Integer> c1 = ()->{
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName() + "| 开始执行任务----");
            return 1;
        };

        // 3. 执行线程任务,获取结果
        Future<Integer> future = executorService.submit(c1);

        // 4.打印结果
        System.out.println(future.get());
    }

}

三、给Runnable添加返回值

如果说我们现在有一个Runnable类型的任务,也想在执行完任务的时候,给调用者返回一个结果。这个时候怎么办呢?Java中也未我们提供了相关的API:

这个问题归根接地是怎么把一个Runnable转为Callable的问题。

代码语言:javascript
复制
package com.lsqingfeng.action.knowledge.multithread.callable;

import java.util.concurrent.*;

/**
 * @className: CallableDemo3
 * @description:
 * @author: sh.Liu
 * @date: 2022-04-08 17:05
 */
public class CallableDemo3 {

    public static void main(String[] args) {
        Runnable r1 = ()->{
            System.out.println("I am Runnable");
        };

        // 转换成Callable:
        Callable<String> c1 = Executors.callable(r1, "result");
        // 然后运行Callable的方式即可:


        // 方式二: 使用FutureTask的方式,传入Runnable和结果
        FutureTask futureTask = new FutureTask(r1, "result2");
        new Thread((futureTask)).start();
        
        // 方式三: 使用线程池, 提交的时候传入Runnable和result
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future<String> result2 = executorService.submit(r1, "result2");
    }
}

好了,关于Callable的内容我们就介绍到这里了。整体来说用法还是比较简单的。就记住有返回值的多线程就是用Callable。如果对你有帮助,记得点点关注。下篇文章我们聊点别的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、基本用法
  • 二、线程池执行Callable
  • 三、给Runnable添加返回值
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档