贾哇技术指南

V1

2022/04/08阅读:20主题:默认主题

如何获取线程返回结果

如何获取线程返回结果

前言:

我们知道 Thread.run() 返回值是 void, 那么当我们通过多线程执行任务的时候,如果想获取线程的返回值, 应该怎么做呢?思路是什么?

1.思路

如果你看过《Thread.start()之后是如何调用 run()方法的呢? 》 这篇文章,会知道当线程启动之后,最终操作系统会回调 Thread.run() 方法。那么我们就可以在 Thread.run() 方法里面做文章。比如我们写了一个类实现了Runnable接口,

public class GetThreadResult implements Runnable {
    @Override
    public void run() {
        hello();
    }

    public String hello() {
        return "Hello";
    }
}

那我们是不是可以用一个全局变量res来接收hello()方法的返回值呢,如果要拿线程的返回值,只需要把res返回即可。 代码如下:

public class GetThreadResult implements Runnable {

    /**
     * 线程结果返回值
     */

    private String res;

    @Override
    public void run() {
        res = hello();
    }

    public String hello() {
        return "Hello";
    }

    public String get() {
        return res;
    }
}

这样我们就可以通过调用get()方法来获取返回值。

2.获取线程调用返回值

上面只是一个简单的思路,还有很多细节待完善,这里简单列举三个:

  1. 结果返回值如何返回通用的类型?
  2. 这样写完全不通用,要在每一个类里面都定义返回值变量,定义get()方法,如果做到通用?
  3. 当我们调用get()方法时,run()方法还没执行到,或者还没执行完怎么办? 下面我们来着重看下如何解决这三个问题。

针对问题1:如何返回通用的类型,我们使用泛型就可以。

针对问题2:如何做到通用的写法呢?《代码之美》第17章有提到:“所有计算机科学领域中的问题都可以通过一个额外的中间层来解决”,所以我们也可以引入中间类来做到通用。

针对问题3:我们可以添加一个状态标记字段,在我们调用get()方法时如果状态是未完成,则让该线程等待(等线程执行完了则唤起该线程);如果状态是已经完成,则直接返回结果即可。

改造后的代码如下:

public class GetThreadResult<Timplements Runnable {

    /**
     * 为了少写一个中间类,这里直接用了函数式接口
     */

    private Supplier<T> supplier;

    private State state;

    public GetThreadResult(Supplier<T> supplier) {
        this.supplier = supplier;
        this.state = State.INIT;
    }

    /**
     * 获取结果的线程
     */

    private Thread getResultThread;

    /**
     * 线程结果返回值
     */

    private T res;

    @Override
    public void run() {
        res = supplier.get();
        this.state = State.COMPLETED;
        if (Objects.nonNull(this.getResultThread)) {
            // unpark 获取结果线程
            LockSupport.unpark(this.getResultThread);
        }
    }

    public T get() {
        // 如果状态是已完成,则直接返回结果
        if (this.state == State.COMPLETED) {
            return res;
        }
        this.getResultThread = Thread.currentThread();
        // park 获取结果线程
        LockSupport.park();
        return res;
    }

    /**
     * 状态
     */

    enum State {
        /**
         * 初始化
         */

        INIT,

        /**
         * 已完成
         */

        COMPLETED;
    }
}

写个程序来检验一下:

public static void main(String[] args) {
        GetThreadResult<String> res = new GetThreadResult<>(() -> {
            System.out.println("当前线程名称:" + Thread.currentThread().getName());
            return "Hello,Thread";
        });
        Thread thread = new Thread(res, "test-get-result-thread");
        thread.start();
        String str = res.get();
        System.out.println("得到线程返回结果:" + str);
    }

输出结果:

当前线程名称:test-get-result-thread
得到线程返回结果:Hello,Thread

这样一来,简易版的获取线程返回结果的程序就写好了。

小结

本篇文章简单实现了获取线程的返回结果,虽然还有很多很多细节需要考虑,比如线程执行中出现了异常怎么处理、如果是线程池中应该怎么处理、如何在获取线程执行结果的时候设置超时等待时间等等,但是我们有了已经总体的方向,如果你有兴趣继续完善,只需要补充里面的细节就好了。你会废了嘛?

好了,这篇文章就到这里了,感谢大家的观看!如有错误,请及时指正!欢迎大家关注我的公众号:贾哇技术指南

分类:

后端

标签:

Java

作者介绍

贾哇技术指南
V1