`
y806839048
  • 浏览: 1126165 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

FutureTask获取线程返回值原理、源码分析

阅读更多

先看一段FutureTask获取线程返回值简单应用的代码,以下基于jdk8进行源码分析。

 

package com.lanhuigu.demo.createthread;

 

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

 

/**

 * 实现Callable接口,获取线程执行返回值

 * @author yihonglei

 * @date 2018/9/12 16:43

 */

public class MyCallable implements Callable<String> {

 

    /**

     * 实现Callable中的call方法

     * @author yihonglei

     * @date 2018/9/12 17:01

     */

    public String call() throws Exception {

        return "Test Callable";

    }

 

    public static void main(String[] args) {

        /** 根据MyCallable创建FutureTask对象 */

        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());

        try {

            /** 启动线程 */

            new Thread(futureTask).start();

            /** 获取线程执行返回值 */

            String s = futureTask.get();

            /** 打印返回值 */

            System.out.println(s);

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

    }

}

程序运行结果:

 

 

成功拿到了线程执行的返回值。

 

以下从源码角度分析拿到返回值的全过程,首先需要简单了解下Callable和FutureTask的结构。

 

Callable是一个函数式接口,源码如下:

 

package java.util.concurrent;

@FunctionalInterface

public interface Callable<V> {

    V call() throws Exception;

}

该接口有一个call方法,返回任意类型值。

 

FutureTask实现RunnableFuture接口,而RunnableFuture继承了Runnable, Future<V>,源码如下:

 

package java.util.concurrent;

import java.util.concurrent.locks.LockSupport;

public class FutureTask<V> implements RunnableFuture<V> {

 ......

}

package java.util.concurrent;

public interface RunnableFuture<V> extends Runnable, Future<V> {

    /**

     * Sets this Future to the result of its computation

     * unless it has been cancelled.

     */

    void run();

}

所以FutureTask具有Runnable和Future功能,因此,在上面的Demo中,以下代码具有Runable特性:

 

/** 根据MyCallable创建FutureTask对象 */

FutureTask<String> futureTask = new FutureTask<>(new MyCallable());

创建线程对象,通过start()方法启动线程:

 

/** 启动线程 */

new Thread(futureTask).start();

start()方法源码如下:

 

public synchronized void start() {

    if (threadStatus != 0)

        throw new IllegalThreadStateException();

 

    group.add(this);

 

    boolean started = false;

    try {

        start0();

        started = true;

    } finally {

        try {

            if (!started) {

                group.threadStartFailed(this);

            }

        } catch (Throwable ignore) {

            /* do nothing. If start0 threw a Throwable then

            it will be passed up the call stack */

        }

    }

}

// 本地方法

private native void start0();

start()方法最后会调用本地方法,由JVM通知操作系统,创建线程,最后线程通过JVM访问到Runnable中的run()方法。

 

而FutureTask实现了Runnable的run()方法,看下FutureTask中的run()方法源码:

 

 

public void run() {

    if (state != NEW ||

        !UNSAFE.compareAndSwapObject(this, runnerOffset,

                                     null, Thread.currentThread()))

        return;

    try {

        /** 

          这里的callable就是我们创建FutureTask的时候传进来的MyCallable对象,

          该对象实现了Callable接口的call()方法。

        */

        Callable<V> c = callable;

        if (c != null && state == NEW) {

            V result;

            boolean ran;

            try {

                /** 

                  调用Callable的call方法,即调用实现类MyCallable的call()方法,

                  执行完会拿到MyCallable的call()方法的返回值“Test Callable”。

                */

                result = c.call();

                ran = true;

            } catch (Throwable ex) {

                result = null;

                ran = false;

                setException(ex);

            }

            if (ran)

                /** 将返回值传入到set方法中,这里是能获取线程执行返回值的关键 */

                set(result);

        }

    } finally {

        // runner must be non-null until state is settled to

        // prevent concurrent calls to run()

        runner = null;

        // state must be re-read after nulling runner to prevent

        // leaked interrupts

        int s = state;

        if (s >= INTERRUPTING)

            handlePossibleCancellationInterrupt(s);

    }

}

从run()方法源码可以知道,MyCallabel执行call()方法的返回值被传入到了一个set()方法中,能拿到线程返回值最关键的

 

就是这个FutureTask的set()方法源码:

 

protected void set(V v) {

    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {

        /** 

          将MyCallable执行call()方法的返回值传进来赋值给了outcome,

          这个outcome是FutureTask的一个成员变量。

          该变量用于存储线程执行返回值或异常堆栈,通过对应的get()方法获取值。

          private Object outcome;

        */

        outcome = v;

        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state

        finishCompletion();

    }

}

到这里,得出一个结论就是,MyCallable执行的call()方法结果通过FutureTask的set()方法存到了成员变量outcome中,

 

通过我们熟悉的get方法就可以获取到outcome对应赋的值。

 

在Demo中获取返回值的代码:

 

/** 获取线程执行返回值 */

String s = futureTask.get();

FutureTask中get()方法的源码:

 

public V get() throws InterruptedException, ExecutionException {

    int s = state;

    if (s <= COMPLETING)

        s = awaitDone(false, 0L);

    /** 调用report方法 */

    return report(s);

}

 

private V report(int s) throws ExecutionException {

    /** outcome赋值给Object x */

    Object x = outcome;

    if (s == NORMAL)

       /** 返回outcome的值,也就是线程执行run()方法时通过set()方法放进去的MyCallable的call()执行的返回值 */

       return (V)x;

    if (s >= CANCELLED)

        throw new CancellationException();

    throw new ExecutionException((Throwable)x);

}

get()方法调用report()方法,report()方法会将outcome赋值并返回,set方法成功拿到返回的outcome,

 

也就是MyCallable()的call()方法执行结果。

 

到这里,我们大概理解了FutureTask.get()能拿到线程执行返回值的本质原理,也就基于FutureTask的成员变量

 

outcome进行的set赋值和get取值的过程。

 

 

 

下面简单总结一下过程:

 

1)FutureTask通过MyCallable创建;

 

2)new Thread()创建线程,通过start()方法启动线程;

 

3)执行FutureTask中的run()方法;

 

4)run()方法中调用了MyCallable中的call()方法,拿到返回值set到FutureTask的outcome成员变量中;

 

5)FutureTask通过get方法获取outcome对象值,从而成功拿到线程执行的返回值;

 

其实,本质上就是一个基于FutureTask成员变量outcome进行的set和get的过程,饶了一圈而已。

--------------------- 

 

原文:https://blog.csdn.net/yhl_jxy/article/details/82664829 

 

分享到:
评论

相关推荐

    有返回值的线程

    标题中的“有返回值的线程”指的是在编程中如何在线程执行完后获取到一个结果。线程通常用于执行异步任务,而当我们需要这些任务执行的结果时,就需要用到带返回值的线程机制。Java语言中,可以通过实现`Callable`...

    java多线程返回值使用示例(callable与futuretask)

    在Java多线程编程中,有时候我们需要从线程...总的来说,`Callable`和`FutureTask`为Java多线程编程提供了一种强大的机制,使得我们能够在线程执行完成后获取返回值和处理异常,这对于实现复杂的并发应用程序至关重要。

    Java线程池FutureTask实现原理详解

    FutureTask实现原理通过submit方法的返回值,为一个Future,实际上这个Future为FutureTask实例,通过此实例,调用get方法,可以阻塞当前线程,直到任务运行完毕,返回结果。整个调用链条如下所示:worker thread -&gt; ...

    Android(Java)之多线程结果返回——Future 、FutureTask、Callable、Runnable

    总结一下,`Runnable`用于无返回值的简单任务,`Callable`用于有返回值的任务,`Future`作为结果的容器,而`FutureTask`则将两者结合,使得`Callable`的任务可以通过`ExecutorService`执行。在实际开发中,根据需求...

    futuretask用法及使用场景介绍

    FutureTask是一种异步获取执行结果或取消执行任务的机制,它可以用来处理耗时的计算任务,使主线程不需要等待计算结果,而是继续执行其他任务。下面将详细介绍FutureTask的用法和使用场景。 一、FutureTask的使用...

    FutureTask学习

    `FutureTask`作为`Runnable`和`Callable`的桥梁,允许我们将一个`Callable`任务提交给`Executor`执行,并在后续代码中通过`FutureTask`的API查询任务状态,获取结果或取消任务。下面将详细介绍`FutureTask`的主要...

    Java中Future、FutureTask原理以及与线程池的搭配使用

    Java中的`Future`和`FutureTask`是并发编程中重要的工具,它们允许程序异步执行任务并获取结果。`Future`接口提供了对异步计算结果的访问和控制,而`FutureTask`是`Future`的一个具体实现,它还同时实现了`Runnable`...

    futuretask源码分析(推荐)

    主要介绍了futuretask源码分析(推荐),小编觉得还是挺不错的,这里给大家分享下,供各位参考。

    FutureTask底层实现分析,有了FutureTask主线程要想获得工作线程(异步计算线程)的结果变得比较简单

    FutureTask 底层实现分析 FutureTask 是 Java 中的一种非常重要的多线程设计模式,用于异步计算线程之间的结果传递。在 JDK 中,FutureTask 类是 Future 模式的实现,它实现了 Runnable 接口,作为单独的线程运行。...

    多线程操作实例源码

    这种方式适用于需要从线程获取返回值的情况。 在多线程操作中,通常会涉及到以下几个关键概念: - **线程同步**:当多个线程访问共享资源时,为了防止数据不一致,需要进行线程同步。Java提供了多种同步机制,如...

    FutureTask:FutureTask原始解析与重组-源码解析

    FutureTask是可取消的异步的计算任务,它可以通过线程池和线程对象执行,一般来说是FutureTask用于耗时的计算。 二,FutureTask继承图 三,未来任务源码 FutureTask的七种状态 状态(state) 值 描述 新的 0 任务...

    比较java中Future与FutureTask之间的关系

    在Java中,Future和FutureTask都是用于获取线程执行的返回结果,但是它们之间存在一些差异和关联。本文将详细介绍Future和FutureTask的关系、使用和分析。 一、Future介绍 Future位于java.util.concurrent包下,是...

    基于Java Callable接口实现线程代码实例

    基于Java Callable接口...Callable接口可以用于获取线程的返回值,对于某些需要获取线程返回值的场景非常有用。同时,本文也提供了一个简单的示例代码,展示了如何使用Callable接口和FutureTask来实现线程的返回值。

    java线程的run()没有返回值怎么办?

    通过这种方式,我们可以在线程执行后获取到一个返回值,解决了`run()`方法没有返回值的问题。这种方法适用于那些需要在主线程中等待子线程完成并获取其结果的场景,例如计算密集型任务或网络请求等。

    Java 多线程与并发(17-26)-JUC线程池- FutureTask详解.pdf

    `FutureTask`是Java并发库中的一个关键组件,它实现了`RunnableFuture`接口,能够作为`Runnable`接口的实现被线程执行,同时也继承了`Future`接口,用于获取计算的结果。`FutureTask`的设计使得我们可以更加灵活地...

    Java中的Runnable,Callable,Future,FutureTask的比较

    Java中的Runnable、Callable、Future和FutureTask是Java多线程编程中的核心概念,它们各自扮演着不同的角色,共同协作以实现并发任务的管理和执行。 1. **Runnable**: Runnable是最基本的多线程接口,它只有一个`...

    spring线程池ThreadPoolExecutor配置以及FutureTask的使用

    结合`FutureTask`,我们可以方便地执行异步任务并获取结果,这对于处理大量并发请求的系统来说非常有用。在实际开发中,根据具体需求灵活配置和使用这些工具,能够提升程序的并发处理能力和响应速度。

    java8源码-concurrency:java并发总结

    获取线程执行结果的原理:以 ThreadPoolExecutor 为例(实现 ExecutorService 接口), 其 submit() 方法提交任务, 返回 一个 FutureTask 实例,这个实例 outcome 成员变量用于存储线程的执行结果, state 成员变量...

    Java并发包源码分析(JDK1.8)

    Java并发包源码分析(JDK1.8):囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包...对每个类的核心源码进行详细分析,笔记详细,由浅入深,层层深入,带您剖析并发编程原理

    Java FutureTask类使用案例解析

    Java FutureTask类是一种异步计算的工具,用于执行长时间的任务并获取结果。它实现了Runnable和Future接口,既可以作为一个Runnable对象提交给Executor执行,也可以作为一个Future对象获取计算结果。 FutureTask类...

Global site tag (gtag.js) - Google Analytics