【Java并发】聊聊Future如何提升商品查询速度

java中可以通过new thread、实现runnable来进行实现线程。但是唯一的缺点是没有返回值、以及抛出异常,而callable就可以解决这个问题。通过配合使用futuretask来进行使用。
并且Future提供了对任务的操作,取消,查询是否完成,获取结果。

Demo

        FutureTask<Integer> futureTask = new FutureTask<Integer>(() -> {
            Thread.sleep(10000);
            System.out.println("调用三方翻译接口");
            return 1024;
        });

        new Thread(futureTask).start();

        Integer integer = futureTask.get();
//        Integer integer = futureTask.get(1, TimeUnit.SECONDS);

        while (true) {
            if (futureTask.isDone()) {
                System.out.println("完成任务");
                break;
            } else {
                System.out.println("执行中,稍等.");
            }
        }
        Integer x = futureTask.get();
        System.out.println(x);

在这里插入图片描述

实现原理

FutureTask核心代码

基本属性

     /* 
     *  Possible state transitions:
     * NEW -> COMPLETING -> NORMAL 任务正常执行,返回结果是正常的结果
     * NEW -> COMPLETING -> EXCEPTIONAL 任务正常执行,但是返回结果是异常
     * NEW -> CANCELLED  任务直接被取消的流程
     * NEW -> INTERRUPTING -> INTERRUPTED  
     */
     // 当前任务的状态
    private volatile int state;
    private static final int NEW          = 0; // 任务的初始化状态
    private static final int COMPLETING   = 1; // Callable的结果,正常封装给当前FutureTask
    private static final int NORMAL       = 2; // Normal任务正常结束
    private static final int EXCEPTIONAL  = 3; // 执行任务时,发生了异常
    private static final int CANCELLED    = 4; // 任务被取消了
    private static final int INTERRUPTING = 5; // 线程的中断状态,被设置为了ture 
    private static final int INTERRUPTED  = 6; // 线程被中断了

    // 当前要执行的任务
    private Callable<V> callable;
    // 存放任务返回结果的属性,也就是futureTask.get 需要获取的结果
    private Object outcome; 
    // 执行任务的线程
    private volatile Thread runner;
    // 单向链表,存放通过get方法挂起等待的线程
    private volatile WaitNode waiters;

任务

当线程start() 之后其实执行的就是call()方法。也就是通过futureTask的run方法执行的call()方法。

    // run方法的执行流程,最终会执行callable的call方法
    public void run() {
        // 保证任务的状态是NEW才执行,或者CAS将当前线程设置为runner.
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
        // 准备执行任务
            Callable<V> c = callable;
            // 任务不为null 并且state==new
            if (c != null && state == NEW) {
                V result; // 返回的结果
                boolean ran; // 任务执行是否正常结束
                try { 
                    // 调用callable的call方法。
                    result = c.call();
                    ran = true; // 正常结束 ran = true
                } catch (Throwable ex) {
                    result = null; // 异常结果为null 
                    ran = false; // 异常结束 ran = false
                    setException(ex); // 设置异常信息
                }
                if (ran) 
                    // 正常执行结束,设置返回结果
                    set(result);
            }
        } finally {
      		// 执行完毕 或者异常 ,都将runner设置为null 
            runner = null;
            // 拿到状态
            int s = state;
            // 如果中断 要做一些事情
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

    //设置result值
    protected void set(V v) {
        // cas设置state为 new-completing
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v; // 返回结果给outcome
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
			// 下面说
            finishCompletion();
        }
    }

get

get方法获取返回结果,到挂起的位置

    public V get() throws InterruptedException, ExecutionException {
        //拿状态
        int s = state;
        //满足未完成状态 就需要等待
        if (s <= COMPLETING)
        // 挂起线程,等待拿结果。
            s = awaitDone(false, 0L);
        return report(s);
    }
    //线程要等待任务执行结束,等待任务执行的状态大于completing状态
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        // dealline get() 就是0 ,如果是get(time,unit) 追加当前系统时间
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        // 构建waitNode 
        WaitNode q = null;
        boolean queued = false;
        //死循环
        for (;;) {
             //判断get线程是否中断了
            if (Thread.interrupted()) {
                //将当前节点从waiter中移除
                removeWaiter(q);
                //并且抛出中断异常
                throw new InterruptedException();
            }
			// 拿到现在任务的状态
            int s = state;
            // 判断任务是否执行完毕
            if (s > COMPLETING) {
                // q != null。表示设置过了 直接移除waitNode线程
                if (q != null)
                    q.thread = null;
                    //返回当前任务状态
                return s;
            }
            // 如果任务的状态处于completing 
            else if (s == COMPLETING) // cannot time out yet
               //让步
                Thread.yield();
            else if (q == null)
                // 线程状态还是new  call方法可能还没有执行 准备挂起线程
                // 封装waitNode存放当前线程
                q = new WaitNode();
            else if (!queued)
                // 如果waitNode还没有排在waiters中,就拍进来(头插法) cas修改
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
                // 如果get(time,unit)挂起线程方式
            else if (timed) {
                // 计算挂起时间
                nanos = deadline - System.nanoTime();
                // 挂起时间 是否小于等于0 
                if (nanos <= 0L) {
                    // 移除waiter 当前node
                    removeWaiter(q);
                    // 返回任务状态
                    return state;
                }
                //正常指定挂起时间即可
                LockSupport.parkNanos(this, nanos);
            }
            else
                //get()挂起线程的方式
                LockSupport.park(this);
        }
    }

   

finishCompletion唤醒线程

线程挂起后,如果任务执行完毕,由finishCompletioon唤醒线程

    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            // 拿到第一个阶段,cas的方式修改 将其设置为null 
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                   // 拿到q线程信息
                    Thread t = q.thread;
                    // 线程信息不为空
                    if (t != null) {
                       // 将waitNode的thread设置为null 
                        q.thread = null;
                        // 唤醒这个线程
                        LockSupport.unpark(t);
                    }
                    //往后遍历 接着唤醒
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    //执行next的waitNode 
                    q = next;
                }
                break;
            }
        }
 
 		//拓展方法 没任何实现 
        done();

		//任务执行完毕
        callable = null;        // to reduce footprint
    }

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);
}

// 正常返回 report
// 异常返回 report
// 取消任务 report
// 中断任务 awaitDone

流程图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/345577.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot整合ElasticSearch实现分页查询

本文使用SpringBoot整合ElasticSearch实现分页查询 文章目录 环境准备分页查询方式一方式二 本文小结 环境准备 还是继续使用spring-boot-starter-data-elasticsearch来实现分页查询操作 <!-- spring-boot-starter-data-elasticsearch--> <dependency><groupId&…

QT解析json数据

QT解析json数据 头文件jsonObjectToMapparseJson结果 头文件 #include <QFile> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> #include <QJsonValue>jsonObjectToMap 将Json对象转换成map QVariantMap MainWindow…

数据分析-Pandas如何用图把数据展示出来

数据分析-Pandas如何用图把数据展示出来 俗话说&#xff0c;一图胜千语&#xff0c;对人类而言一串数据很难立即洞察出什么&#xff0c;但如果展示图就能一眼看出来门道。数据整理后&#xff0c;如何画图&#xff0c;画出好的图在数据分析中成为关键的一环。 数据表&#xff…

详解JavaScript异步编程之Promise

一、前言 JavaScript是⼀⻔典型的异步编程脚本语⾔&#xff0c;在编程过程中会⼤量的出现异步代码的编写&#xff0c;在JS的整个发展历程中&#xff0c;对异步编程的处理⽅式经历了很多个时代&#xff0c;其中最典型也是现今使⽤最⼴泛的时代&#xff0c;就是Promise对象处理异…

当软件开发具备了低代码的开发能力,难以想象会有多“香”

一、前言 低代码开发平台&#xff0c;一个号称能在几分钟的时间里开发出一套公司内部都可使用的应用系统开发工具。 很多人或许都隐隐听说过低代码&#xff0c;因为低代码不仅远名国外&#xff0c;国内的腾讯、阿里、华为、网易、百度等科技巨头也纷纷入局。 那么市面上都有哪些…

全桥RLC模态图具体分析

T0时刻&#xff0c;Q6,Q7,Q1.Q4开通&#xff0c;驱动为高电平&#xff0c;励磁电流线性上升,但是lm电流在to是为负电流&#xff0c;这时刻有给副边提供能量&#xff0c;Ip电流开始上升&#xff0c;这个时候给副边的电流也是从0开始上升,这个能量由励磁电感提供&#xff0c;Co给…

HCIA——27E-mall、MIME;POP3、IMAP的选择,解答

学习目标&#xff1a; 计算机网络 1.掌握计算机网络的基本概念、基本原理和基本方法。 2.掌握计算机网络的体系结构和典型网络协议&#xff0c;了解典型网络设备的组成和特点&#xff0c;理解典型网络设备的工作原理。 3.能够运用计算机网络的基本概念、基本原理和基本方法进行…

整理了一下常用的LaTeX数学公式语法,未完待续

为了方便对应&#xff0c;后面会拆一下 公式代码放入LaTeX编译环境中时&#xff0c;两边需要加入$$: $$公式代码$$ 1&#xff0c;分解示例 L^{A}T_{E}X\,2_{\epsilon} c^{2}a^{2}b^{2} \tau\phi \cos2\pi1 f\, \,a^{x}\,\,b \heartsuit \cos^{2}\theta \sin^{2}\theta 1.0…

Nodejs前端学习Day1

妈的&#xff0c;学vue3需要15.0以上的nodejs 文章目录 前言一、学习目标二、学习目录三、为什么JavaScript可以在浏览器中被执行四、为什么JavaScript可以操作DOM和BOM五、浏览器中的JavaScript运行环境总结 前言 妈的&#xff0c;学vue3需要15.0以上的nodejs 一、学习目标 二…

CNN经典网络模型(五):ResNet简介及代码实现(PyTorch超详细注释版)

目录 一、开发背景 二、网络结构 三、模型特点 四、代码实现 1. model.py 2. train.py 3. predict.py 4. spilit_data.py 五、参考内容 一、开发背景 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的&#xff0c; 斩获2015年ImageNet竞赛…

SE通道注意力机制模块

简介 论文原址&#xff1a;https://arxiv.org/pdf/1709.01507.pdf 在深度学习领域&#xff0c;提升模型的表征能力一直是一个关键的研究方向。SE&#xff08;Squeeze-and-Excitation&#xff09;模块是一种引入通道注意力机制的方法&#xff0c;旨在让神经网络更加关注对当前…

我每天如何使用 ChatGPT

我们都清楚互联网的运作方式——充斥着各种“爆款观点”&#xff0c;极端分裂的意见&#xff0c;恶搞和无知现象屡见不鲜。 最近&#xff0c;大家对于人工智能&#xff08;AI&#xff09;特别是大语言模型&#xff08;LLMs&#xff09;和生成式 AI&#xff08;GenAI&#xff0…

【趣味游戏-08】20240123点兵点将点到谁就是谁(列表倒置reverse)

背景需求&#xff1a; 上个月&#xff0c;看到大4班一个孩子在玩“点兵点将点到谁就是谁”的小游戏&#xff0c;他在桌上摆放两排奥特曼卡片&#xff0c;然后点着数“点兵点将点到谁就是谁”&#xff0c;第10次点击的卡片&#xff0c;拿起来与同伴的卡片进行交换。他是从第一排…

Unity-Arduino Bluetooth Plugin蓝牙插件使用时需要注意的一些事项(附插件下载链接)

一些参考链接 1.Android 无法扫描蓝牙设备踩坑 2.权限相关 1-首先要明确你的蓝牙设备是经典蓝牙还是低功耗&#xff08;BLE)蓝牙&#xff1a; 转载&#xff1a;Android蓝牙开发—经典蓝牙和BLE&#xff08;低功耗&#xff09;蓝牙的区别 2.如果是BLE蓝牙&#xff0c;需要打勾…

what is `ContentCachingRequestWrapper` does?

ContentCachingRequestWrapper 是 Spring Framework 中提供的一种包装类&#xff0c;它扩展了 HttpServletRequestWrapper 类&#xff0c;用于缓存请求体的内容。 通常在处理 HTTP 请求时&#xff0c;原生的 HttpServletRequest 对象中的输入流 (getInputStream()) 只能被读取一…

SpringBoot-多数据源切换和事物处理(免费)

作者原始文章: SpringBoot-多数据源切换和事物处理 最新内容和改动请看上面的文章 安装 <dependency><groupId>com.gitee.huanminabc</groupId><artifactId>dynamic-datasource</artifactId><version>1.0.3-RELEASE</version> <…

【经验分享】豆瓣小组的文章/帖子怎么删除?

#豆瓣小组的文章/帖子怎么删除&#xff1f;# 第一步&#xff1a; 手机登录豆瓣app ↓ 点右下角“我” ↓ 然后在页面点击我的小组 ↓ 点我发布的 ↓ ↓ 再任意点开一个帖子 ↓ 在文章和帖子的右上角有一个笔状的图标&#xff0c;切记不是右上角的横三点… ↓ ↓ 最后点下边的…

git 对象压缩及垃圾对象清理

git 对象压缩及垃圾对象清理 这篇文章让我们来看看 git 的对象压缩机制&#xff0c;前面的几篇文章我们提到&#xff0c;在执行 git add 命令会会把文件先通过 zlib 压缩后放入到「暂存区」&#xff0c;我们先看看这个步骤&#xff1a; 我们这个实例中有一个 1.28m 的 index.…

6.php开发-个人博客项目Tp框架路由访问安全写法历史漏洞

目录 知识点 php框架——TP URL访问 Index.php-放在控制器目录下 ​编辑 Test.php--要继承一下 带参数的—————— 加入数据库代码 --不过滤 --自己写过滤 --手册&#xff08;官方&#xff09;的过滤 用TP框架找漏洞&#xff1a; 如何判断网站是thinkphp&#x…

​比特币大跌的 2 个原因

撰文&#xff1a;秦晋 原文来自Techub News&#xff1a;​比特币大跌的 2 个原因 比特币迎来大跌&#xff01;1 月 23 日凌晨&#xff0c;比特币跌破 40000 美元&#xff0c;为去年 12 月 4 日以来首次&#xff0c;日内跌超 3%。这是自 1 月 10 日美国证监会审批通过 11 只比…