JUC-线程Callable使用与FutureTask源码阅读

这篇具有很好参考价值的文章主要介绍了JUC-线程Callable使用与FutureTask源码阅读。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

JUC-线程Callable使用与FutureTask源码阅读

Callable简单使用

带返回值的线程(实现implements Callable<返回值类型>),使用示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;

/**

- 实现Callable<T> 接口

- 含返回值且可抛出异常的线程创建启动方式

- @author fatah
  */
  public class Demo5 implements Callable<String>{

  public String call() throws Exception {
      System.out.println("正在执行新建线程任务");
      Thread.sleep(2000);
      return "新建线程睡了2s后返回执行结果";
  }

  public static void main(String[] args) throws InterruptedException, ExecutionException {
      Demo5 d = new Demo5();
      /*    call()只是线程任务,对线程任务进行封装
          class FutureTask<V> implements RunnableFuture<V>
          interface RunnableFuture<V> extends Runnable, Future<V>
      */
      FutureTask<String> task = new FutureTask<>(d);
      Thread t = new Thread(task);
      t.start();
      System.out.println("提前完成任务...");
      //获取任务执行后返回的结果
      String result = task.get();
      System.out.println("线程执行结果为"+result);
  }

}
FutureTask面向对象方式学习

为了定义这样一个事物“有返回结果”,暂且称之为RunnableFuture。它集合了Runnable和Future两种事物

(其中Future接口 表示了一个任务的生命周期,是一个可取消的异步运算,可以把它看作是一个异步操作的结果的占位符,它将在未来的某个时刻完成,并提供对其结果的访问。在并发包中许多异步任务类都继承自Future)

JUC-线程Callable使用与FutureTask源码阅读

其中

  • Future接口主要使用get()方法获取线程任务返回值

  • Runnable接口的run()方法是执行线程任务主体

public interface Runnable {
    public abstract void run();
}


public interface Future<V> {
	//取消任务
    boolean cancel(boolean mayInterruptIfRunning);
 
    boolean isCancelled();
    
    //任务是否完成
    boolean isDone();
    
    // 获取任务完成情况
    V get() throws InterruptedException, ExecutionException;
 
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}




public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}


结合两个接口创造出RunnableFuture接口

FutureTask构造方法

当使用new FutureTask()构造对象时,需要Callable接口类。如果是Runnable接口,使用适配器转换为Callable类型对象。*

  • FutureTask(Callable callable)
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}
  • FutureTask(Runnable runnable, V result)

此处使用了适配器模式,通过工具方法Executors.callable方法把Runnable转化为Callable调用

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

Executors.java中

    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

    /**
     * 运行给定任务并返回给定结果的可调用对象
     */
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

其中callable是FutureTask内部私有变量

    private Callable<V> callable;
Run方法-RunnableFuture接口实现的

Callable c = callable;

result = c.call();

1.此处调用函数式接口Callable业务**call()**方法

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();1.重点!!!)
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);2.重点!!!)
            }
        } 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);
        }
    }

运行任务,如果任务状态为NEW状态,则利用CAS修改为当前线程。

2.业务任务执行完毕调用set(result)方法设置执行结果。set(result)源码如下:

其中outcome为FutureTask私有变量

private Object outcome;
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
Get()方法-Future接口实现
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

说明:FutureTask 通过get()方法获取任务执行结果。如果任务处于未完成的状态(state <= COMPLETING),就调用awaitDone方法(后面单独讲解)等待任务完成。任务完成后,通过report方法获取执行结果或抛出执行期间的异常。report源码如下。

    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

可以看到使用get()方法会调用awaitDone等待线程完成,同时响应线程“取消”等操作。

可以看到report中 return (V)x; 即run方法中存储线程任务执行返回结果outcome文章来源地址https://www.toymoban.com/news/detail-441723.html

到了这里,关于JUC-线程Callable使用与FutureTask源码阅读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JUC编程之——线程的start方法及底层源码

    简单的写一个线程的小栗子: 查看java start()方法的源码: 更加底层的C++代码: thread.c (代码位置:openJDKsrcsharenativejavalang) jvm.cpp (代码位置:openIDKhotspotsrcsharevmprims) thread.cpp (代码位置:openJDKhotspotsrcsharevmruntime) 代码——OpenJDK源码网址:http://openjdk.java.net/ 2.3.1 thre

    2023年04月13日
    浏览(40)
  • JUC并发编程——集合类不安全及Callable(基于狂神说的学习笔记)

    List不安全 CopyOnWriteArrayList与vector对比,以下来自CSDN智能助手的回答: Java中的CopyOnWriteArrayList和Vector都是线程安全的动态数组,可以在多线程环境下使用。 CopyOnWriteArrayList使用了一种特殊的写时复制机制,它在对数组进行修改时,会创建一个新的副本,而不是直接在原数组上

    2024年02月07日
    浏览(73)
  • 妙用 FutureTask + 线程池:轻松解决接口超时问题!

    来源:blog.csdn.net/qq_44384533/article/details/112324224 之前红包权益领取查询的接口超时了,因为有用户订购的权益有点多 用线程池+ FutureTask将1个查询拆分成多个小查询 选择FutureTask是因为它具有仅执行1次run()方法的特性(即使有多次调用也只执行1次),避免了重复查询的可能。而且

    2024年02月05日
    浏览(38)
  • 多线程-线程的创建的方式3、4:实现Callable与线程池

    简要概况: 案例: 输出: 现有问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。 那么,有没有一种办法使得线程可以复用,即执行完一个任务,并

    2024年02月09日
    浏览(45)
  • JavaEE(系列14) -- 多线程(Callable)

    Callable 是一个 interface . 相当于把线程封装了一个 \\\"返回值\\\". 方便程序猿借助多线程的方式计算结果. 代码示例 : 创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本 思路 : 创建一个类Result,包含 sum 表示最终结果, lock 表示线程同步使用的锁对象. main 方法中先创建 Result 实例,

    2024年02月06日
    浏览(35)
  • 多线程-Runable和Callable的区别

    在Java中,多线程可以通过实现Runnable接口或使用Callable接口来实现。这两种方式有一些区别,如下所示: 返回值: Runnable接口的run()方法没有返回值,它表示一个没有返回结果的任务。 Callable接口的call()方法有返回值,可以返回计算结果。 异常处理: Runnable接口的run()方法不

    2024年02月14日
    浏览(38)
  • 多线程之Runnable 、Callable 、Thread

    Java 提供了三种创建线程方法: 通过实现 Runnable 接口; 通过继承 Thread 类本身; 通过 Callable 和 Future 创建线程。 创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。 为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下: 你可以重写该方法,重要的

    2024年02月07日
    浏览(52)
  • JUC之线程、线程池

    start方法开启一个新线程,异步执行。 run方法同步执行,不会产生新的线程。 start方法只能执行一次,run方法可以执行多次。 sleep() 线程睡眠 两种方式调用: 线程打断 interrupt()这种方式打断,线程会抛出InterruptedException异常。比如在线程睡眠的时候,调用interrupt(),线程抛出

    2024年04月27日
    浏览(33)
  • 从源码角度深入解析Callable接口

    摘要: 从源码角度深入解析Callable接口,希望大家踏下心来,打开你的IDE,跟着文章看源码,相信你一定收获不小。 本文分享自华为云社区《一个Callable接口能有多少知识点?》,作者: 冰 河。 并发编程一直是程序员们比较头疼的,如何编写正确的并发程序相比其他程序来

    2023年04月18日
    浏览(46)
  • 【JUC-1】java多线程线程基础知识

    继承Thread类. 实现Runable接口. 实现Callable接口. Runable/Callable接口的实现, 都是重写其中的run/call方法, 实现任务逻辑, 再由线程执行器(可以是Thread类,也可以是线程池)并发执行run/call的逻辑. 而Thread类中的包含start方法, 可以控制线程启动,执行任务. 当发生线程上下文切换时, 操作系

    2024年02月11日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包