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

news2024/9/27 9:22:33

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.coloradmin.cn/o/1408269.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

XPath常用定位方式

1、通常定位元素有比较固定的八种定位方式&#xff0c;如下图&#xff1b; 2、平时可以通过浏览器右键进行获取定位方式&#xff0c;但是通常获取的元素无法准确定位或者太长这时就需要自己通过XPath语法来进行定位&#xff1b;目前我这边记录两种常用的定位方式&#xff0c;第…

【大数据】流处理基础概念(二):时间语义(处理时间、事件时间、水位线)

流处理基础概念&#xff08;一&#xff09;&#xff1a;Dataflow 编程基础、并行流处理流处理基础概念&#xff08;二&#xff09;&#xff1a;时间语义&#xff08;处理时间、事件时间、水位线&#xff09;流处理基础概念&#xff08;三&#xff09;&#xff1a;状态和一致性模…

10. Profile

1. 区分环境的配置 1.1. properties 配置 假设&#xff0c;一个应用的工作环境有&#xff1a;dev、test、prod 那么&#xff0c;我们可以添加 4 个配置文件&#xff1a; applcation.properties - 公共配置application-dev.properties - 开发环境配置application-test.proper…

电脑文件mfc140.dll丢失的解决方法指导,怎么快速修复mfc140.dll

mfc140.dll 文件的缺失是个普遍的问题&#xff0c;在日常使用中可能会时不时遇到。本文主要目的是详细介绍一旦遇到 mfc140.dll 文件缺失&#xff0c;应该如何进行下载和安装的步骤。不再赘言&#xff0c;下面就一起深入了解mfc140.dll丢失的解决方法指导。 一. mfc140.dll的作…

跟着pink老师前端入门教程-day09

二十二、定位 22.1 为什么需要定位 1. 某个元素可以自由的在一个盒子内移动位置&#xff0c;并且压住其他盒子 2. 当我们滚动窗口时&#xff0c;盒子是固定屏幕某个位置的 解决方法&#xff1a; 1. 浮动可以让多个块级盒子一行没有缝隙排列显示&#xff0c;经常用于横向排…

Spring Boot开发Spring Security

这里我对springboot不做过多描述&#xff0c;因为我觉得学这个的肯定掌握了springboot这些基础 导入核心依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring‐boot‐starter‐security</artifactId> </depen…

如何优雅的实现前端国际化?

JavaScript 中每个常见问题都有许多成熟的解决方案。当然&#xff0c;国际化 (i18n) 也不例外&#xff0c;有很多成熟的 JavaScript i18n 库可供选择&#xff0c;下面就来分享一些热门的前端国际化库&#xff01; i18next i18next 是一个用 JavaScript 编写的全面的国际化框架…

Apipost自动化测试+Jenkins实现持续集成

Apipost 自动化测试支持「持续集成」功能&#xff0c;在安装了Apipost的服务器中输入命令&#xff0c;即可运行测试脚本。 创建自动化测试脚本 在创建好的测试用例中选择「持续集成」。 点击新建&#xff0c;配置运行环境、循环次数、间隔停顿后点击保存会生成命令。 安装 Ap…

C++ STL之stack的使用及模拟实现

文章目录 1. 介绍2. stack的使用3. 栈的模拟实现 1. 介绍 英文解释&#xff1a; 也就是说&#xff1a; stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 stack是作为容器适配器被实现…

Java JVM垃圾回收 JVM调优 老年代 新生代

如何判断对象可以回收 引用计数法 当一个对象被其他对象引用&#xff0c;该对象计数 1&#xff0c;当某个对象不再引用该对象&#xff0c;其计数 -1当一个对象没有被其他对象引用时&#xff0c;即计数为0&#xff0c;该对象就可以被回收 缺点&#xff1a;循环引用时&#xf…

全桥RLC模态图具体分析

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

HCIP-BGP实验4

搭建实验拓扑图 要求 1.全网可达 2.isp只能配置IP地址 实验开始 配置IP地址及环回 r1,r2,r9,r10配ipv4地址(以r1为例) [Huawei]sysname r1 [r1]interface g0/0/0 [r1-GigabitEthernet0/0/0]ip address 12.1.1.1 24 [r1-GigabitEthernet0/0/0]q [r1]interface LoopBack 0…

【Foxmail】客户端发送邮件错误:SSL Recv :服务器断开连接, errorCode: 6

Foxmail客户端发送邮件提示&#xff1a;SSL Recv :服务器断开连接, errorCode: 6 错误代码 处理方式&#xff1a; 去邮箱生成新的16位授权码&#xff0c;输入到 密码框 内即可。 注&#xff1a;一旦开通授权码&#xff0c;在Foxmail验证时 密码框 里输入的就是 授权码

Ddosify 作为压测工具的使用指南

文章目录 1. 写在最前面1.1 Kubernetes 监控1.2 Performance Testing 2. 命令行安装 & 使用2.1 安装2.2 使用2.2.1 默认的例子2.2.2 定制的例子 3. Dashboard 安装 & 使用3.1 安装3.2 使用3.2.1 简单使用3.2.3 依赖的服务介绍 4. 碎碎念5. 参考资料 1. 写在最前面 由于…

【单例模式】保证线程安全实现单例模式

&#x1f4c4;前言&#xff1a;本文是对经典设计模式之一——单例模式的介绍并讨论单例模式的具体实现方法。 文章目录 一. 什么是单例模式二. 实现单例模式1. 饿汉式2. 懒汉式2.1 懒汉式实现单例模式的优化&#xff08;一&#xff09;2.2 懒汉式实现单例模式的优化&#xff08…

EI论文复现:考虑冷热运行特性的综合能源系统多时间尺度优化调度程序代码!

适用平台/参考文献&#xff1a;MatlabYalmipCplex&#xff1b; 参考文献&#xff1a;电力系统自动化《含冰蓄冷空调的冷热电联供型微网多时间尺度优化调度》 提出考虑冷热特性的综合能源系统多时间尺度优化调度模型&#xff0c;日前计划中通过多场景描述可再生能源的不确定性…

表白墙网站PHP源码,支持封装成APP

源码介绍 PHP表白墙网站源码&#xff0c;适用于校园内或校区间使用&#xff0c;同时支持封装成APP。告别使用QQ空间的表白墙。 简单安装&#xff0c;只需PHP版本5.6以上即可。 通过上传程序进行安装&#xff0c;并设置账号密码&#xff0c;登录后台后切换模板&#xff0c;适配…

牛客30道题解析精修版

1.异常处理 都是Throwable的子类&#xff1a; ① Exception&#xff08;异常&#xff09;:是程序本身可以处理的异常。 ② Error&#xff08;错误&#xff09;: 是程序无法处理的错误。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时&#xff0c;一般不需要…

《统计学习方法:李航》笔记 从原理到实现(基于python)-- 第3章 k邻近邻法

文章目录 第3章 k邻近邻法3.1 k近邻算法3.2 k近邻模型3.2.1 模型3.2.2 距离度量3.2.3 k值的选择3.2.4 分类决策规则 3.3 k近邻法的实现&#xff1a;kd树3.3.1 构造kd树3.3.2 搜索kd树 算法实现课本例3.1iris数据集scikit-learn实例kd树:构造平衡kd树算法例3.2 《统计学习方法&a…

把Windows系统装进U盘,到哪都有属于你自己的电脑系统

前言 自从接触到WinPE启动盘之后&#xff0c;小白就突然萌生了一个想法&#xff1a;为啥不能把完整的Windows放进U盘呢&#xff1f; 实际上Windows是可以安装进U盘的&#xff0c;外出的时候带上&#xff0c;只需要有台正常开机的电脑就可以使用属于自己的系统。 这个是早已经…