码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • JAVA多线程FutureTask作用



    1.什么是FutureTask


              FutureTask是一个可取消的异步运算的任务,FutureTask里面可以可以传入Callable和Runable实现类作为参数,可以对异步运算任务的结果进行等待获取,判断是否已经完成,取消任务等操作。理解FutureTask之前先要知道Future接口,

              Future接口属于包java.util.concurrent, 接口提供了判断任务是否完成、终端任务、获取任务执行结果三种能力,源码如下:

    1. package java.util.concurrent;
    2. public interface Future {
    3. //用来取消任务
    4. boolean cancel(boolean mayInterruptIfRunning);
    5. //返回任务是否取消成功
    6. boolean isCancelled();
    7. // 返回任务是否执行完成
    8. boolean isDone();
    9. //用来获取执行结果,获取的时候产生阻塞,直到任务执行完成
    10. V get() throws InterruptedException, ExecutionException;
    11. //用来获取任务执行结果,获取的时候产生阻塞,指定时间内任务没有执行完则TimeoutException
    12. V get(long timeout, TimeUnit unit)
    13. throws InterruptedException, ExecutionException, TimeoutException;
    14. }

            Future只是一个接口,我们经常使用的是java.util.concurrent.FutureTask, 它是Future接口的实现类  (FutureTask首先实现了RunnableFuture接口,RunnableFuture接口集成Future.并在Future接口的基础上增加了run()行为)

            因为接口Future#get()是阻塞方法,所以我们在使用FutureTask#get()获取线程处理结果的时,先会阻塞等待任务处理完成, 处理完成后才能拿到结果

    1. public class FutureTask implements RunnableFuture {
    2. //...
    3. }
    1. public interface RunnableFuture extends Runnable, Future {
    2. /**
    3. * Sets this Future to the result of its computation
    4. * unless it has been cancelled.
    5. */
    6. void run();
    7. }

            一个Future对象可以调用Callable和Runable的对象进行包装,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中,比如查看Spring源码中的ThreadPoolTaskExecutor 对象

    1. public class ThreadPoolTaskExecutor extends CustomizableThreadFactory
    2. implements SchedulingTaskExecutor, Executor, BeanNameAware, InitializingBean, DisposableBean {
    3. public void execute(Runnable task) {
    4. Executor executor = getThreadPoolExecutor();
    5. try {
    6. executor.execute(task);
    7. }
    8. catch (RejectedExecutionException ex) {
    9. throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
    10. }
    11. }
    12. public Future submit(Runnable task) {
    13. FutureTask future = new FutureTask(task, null);
    14. execute(future); //future 为参数
    15. return future;
    16. }
    17. public Future submit(Callable task) {
    18. FutureTask future = new FutureTask(task);
    19. execute(future); //future 为参数
    20. return future;
    21. }
    22. }
    23.         在项目中FutrueTask 广泛使用, 比如有一个任务处理起来比较慢,但是这个任务可以拆分成多个小任务分别处理,然后将结果合并起来返回, 

              举个例子:计算1*10+2*10+3*10+4*10+5*10求和,可以一次计算出来,也可以拆分成多个线程计算N*10, 每个线程处理后,在最后求和。如下代码所示

      1. package com.thread.future;
      2. import java.util.ArrayList;
      3. import java.util.Date;
      4. import java.util.List;
      5. import java.util.concurrent.*;
      6. public class FutureTaskTest {
      7. public static void main(String[] args) throws ExecutionException, InterruptedException {
      8. ExecutorService exec = Executors.newFixedThreadPool(5);
      9. List> taskList = new ArrayList>();
      10. for (int i = 1; i < 6; i++) {
      11. FutureTask task = new FutureTask(new Calculate(i));
      12. exec.execute(task);
      13. taskList.add(task);
      14. }
      15. Integer sum = 0;
      16. for (FutureTask task : taskList) {
      17. //阻塞等待,当线程所有任务执行后才能达到每个线程处理结果
      18. Integer num = task.get();
      19. //输出处理结果
      20. System.out.println("" + new Date() + " " + sum + "+" + num + "=" + (sum + num));
      21. sum = sum + num;
      22. }
      23. exec.shutdown();
      24. }
      25. }
      26. class Calculate implements Callable {
      27. private int a;
      28. public Calculate(Integer a) {
      29. this.a = a;
      30. }
      31. public Integer call() throws Exception {
      32. System.out.println("" + new Date() + " " + Thread.currentThread().getName() + "deal :" + a);
      33. Thread.sleep(2000);
      34. return a * 10; //这里只模拟简单运算
      35. }
      36. }
      【并发编程】 — Runnable、Callable、Future和FutureTask之间的关系
      pdf 0星 超过10%的资源 311KB
      下载

      执行结果

      1. Fri Jul 29 19:17:03 CST 2022 pool-1-thread-5deal :5
      2. Fri Jul 29 19:17:03 CST 2022 pool-1-thread-2deal :2
      3. Fri Jul 29 19:17:03 CST 2022 pool-1-thread-1deal :1
      4. Fri Jul 29 19:17:03 CST 2022 pool-1-thread-4deal :4
      5. Fri Jul 29 19:17:03 CST 2022 pool-1-thread-3deal :3
      6. Fri Jul 29 19:17:05 CST 2022 0+10=10
      7. Fri Jul 29 19:17:05 CST 2022 10+20=30
      8. Fri Jul 29 19:17:05 CST 2022 30+30=60
      9. Fri Jul 29 19:17:05 CST 2022 60+40=100
      10. Fri Jul 29 19:17:05 CST 2022 100+50=150

      2. FutureTask源码分析


      FutureTask中 state用volatile修饰的,如果在多线程并发的情况下,某一个线程改变了任务的状态,其他线程都能够立马知道,保证了state字段的可见性

      1. public class FutureTask implements RunnableFuture {
      2. // 表示当前任务的状态,volatile修饰
      3. private volatile int state;
      4. // 表示当前任务的状态是新创建的,尚未执行
      5. private static final int NEW = 0;
      6. // 表示当前任务即将结束,还未完全结束,值还未写,一种临界状态
      7. private static final int COMPLETING = 1;
      8. // 表示当前任务正常结束
      9. private static final int NORMAL = 2;
      10. // 表示当前任务执行过程中出现了异常,内部封装的callable.call()向上抛出异常了
      11. private static final int EXCEPTIONAL = 3;
      12. // 表示当前任务被取消
      13. private static final int CANCELLED = 4;
      14. // 表示当前任务中断中
      15. private static final int INTERRUPTING = 5;
      16. // 表示当前任务已中断
      17. private static final int INTERRUPTED = 6;
      18. }

      构造器:

      FuturnTask提供了两个构造器,FutureTask里面可以可以传入Callable和Runable实现类作为参数

      java多线程编程同步器Future和FutureTask解析及代码示例
      pdf 0星 超过10%的资源 73KB
      下载
      1. /
      2. //上文说FutureTask里面可以可以传入Callable和Runable实现类作为参数
      3. //一旦运行,执行给定的Callable
      4. public FutureTask(Callable callable) {
      5. if (callable == null)
      6. throw new NullPointerException();
      7. this.callable = callable;
      8. // 设置状态为新创建
      9. this.state = NEW;
      10. }
      11. //一旦运行,执行给定的Runalbe,并在完成后通过get返回给调用者
      12. public FutureTask(Runnable runnable, V result) {
      13. this.callable = Executors.callable(runnable, result);
      14. // 设置状态为新创建
      15. this.state = NEW;
      16. }

      run方法:

      1. public void run() {
      2. // 当前任务状态不为new或者runner旧值不为null,说明已经启动过了,直接返回,这里也说明了run()里面的具体逻辑只会
      3. if (state != NEW ||
      4. !UNSAFE.compareAndSwapObject(this, runnerOffset,
      5. null, Thread.currentThread()))
      6. return;
      7. // 只有当任务状态为new并且runner旧值为null才会执行到这里
      8. try {
      9. // 传入的callable任务
      10. Callable c = callable;
      11. // 当任务不为null并且当前任务状态为新建时才会往下执行
      12. if (c != null && state == NEW) {
      13. V result; // 储存任务的返回结果
      14. boolean ran;// 储存执行是否成功
      15. try {
      16. // 调用callable.run()并返回结果
      17. result = c.call();
      18. ran = true; // 正常执行设置ran为true
      19. } catch (Throwable ex) {
      20. result = null; // 异常时设置结果为null
      21. ran = false;
      22. //并且更新任务状态为EXCEPTIONAL(执行过程中出现了异常)并且唤醒阻塞的线程
      23. setException(ex);
      24. }
      25. if (ran) //执行成功
      26. // 内部设置outcome为callable执行的结果,并且更新任务的状态为NORMAL(任务正常执行)并且唤醒阻塞的线程
      27. set(result);
      28. }
      29. } finally {
      30. // 将当前任务的线程设置为null
      31. runner = null;
      32. // 当前任务的状态
      33. int s = state;
      34. // 如果state>=INTERRUPTING,说明当前任务处于中断中或已中断状态
      35. if (s >= INTERRUPTING)
      36. // 如果当前任务处于中,则执行这个方法线程会不断让出cpu直到任务处于已中断状态
      37. handlePossibleCancellationInterrupt(s);
      38. }
      39. }

      上一篇:【JAVA多线程】线程变量ThreadLocal和InheritableThreadLocal

    24. 相关阅读:
      WebGL笔记:WebGL中的顶点着色器尺寸,颜色的修改
      PYTHON中的字符编码
      【Linux集群教程】10 集群监控 - Nagios 搭建
      2. Mybatis流程
      创建文件练习
      openGauss学习笔记-77 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT特性及价值
      Node.js躬行记(23)——Worker threads
      Unity的粒子总是丢材质
      ArcGIS Pro和ArcGIS有什么区别和联系,优势有哪些?
      浅析无人机发展趋势以及如何实现EasyDSS+无人机视频推流?
    25. 原文地址:https://blog.csdn.net/Beijing_L/article/details/126041470
      • 最新文章
      • 攻防演习之三天拿下官网站群
        数据安全治理学习——前期安全规划和安全管理体系建设
        企业安全 | 企业内一次钓鱼演练准备过程
        内网渗透测试 | Kerberos协议及其部分攻击手法
        0day的产生 | 不懂代码的"代码审计"
        安装scrcpy-client模块av模块异常,环境问题解决方案
        leetcode hot100【LeetCode 279. 完全平方数】java实现
        OpenWrt下安装Mosquitto
        AnatoMask论文汇总
        【AI日记】24.11.01 LangChain、openai api和github copilot
      • 热门文章
      • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
        奉劝各位学弟学妹们,该打造你的技术影响力了!
        五年了,我在 CSDN 的两个一百万。
        Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
        面试官都震惊,你这网络基础可以啊!
        你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
        心情不好的时候,用 Python 画棵樱花树送给自己吧
        通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
        13 万字 C 语言从入门到精通保姆级教程2021 年版
        10行代码集2000张美女图,Python爬虫120例,再上征途
      Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
      正则表达式工具 cron表达式工具 密码生成工具

      京公网安备 11010502049817号