Java多线程篇(13)——FutureTask、Disruptor的使用

文章目录

  • FutureTask
    • CompletionService
    • CompletableFuture
  • Disruptor
    • Disruptor 核心概念
    • 运行流程
    • 不同生产者模式的区别
    • Disruptor设计精髓

FutureTask

现有一个场景,10个线程执行10个任务,然后主线程获取任务结果。

比较广泛的一个说法就是,runnable是没有返回值的线程,callable是有返回值的线程。所以最先想到的是用callable接口去获取线程返回值。

实际上,"runnable是没有返回值的线程,callable是有返回值的线程"这种说法。
我个人认为是错误的,我认为运行线程的方式只有一种,就是实现runnable接口!不管你是new Thread,还是使用线程池最终都要实现runnable接口。就算是用callable接口的方式也不例外。

callable的返回值是如何实现的?
提交的callable作为成员变量封装到RunnableFuture(最常用的实现类就是FutureTask),而RunnableFuture又继承自Runnable,所以其实线程池真正提交的还是一个Runnable(RunnableFuture)。
RunnableFuture.run方法调用了callable.call方法,并将call方法结果存起来,唤醒等待结果的线程。
RunnableFuture.get方法如果有结果了就直接返回,如果没有就自旋/阻塞等待唤醒。

在这里插入图片描述

CompletionService

单纯使用FutureTask有一个最大的问题就是,在获取任务结果的时候,如果前一个任务还没有结果,即使后面的任务有结果了也无法打印出来。所以有没有那么一种办法,可以让10个任务,谁先完成了就谁先打印。因此,CompletionService 来了。
在这里插入图片描述
这个实现原理不用看源码也基本可以猜到就是在原来的基础上多加一个阻塞队列,将任务结果统一存入阻塞队列,先进先出。

CompletableFuture

FutureTask还有一个问题就是会阻塞主线程。所以有没有那么一种办法,可以不阻塞主线程(异步回调)。主线程只管提交任务,提交完后就不管了,无需等待任务结果,任务完成后自己回调后续操作。因此,CompletableFuture 来了。
在这里插入图片描述
另外,CompletableFuture还支持串行执行
在这里插入图片描述

通过打印的信息得知,CompletableFuture使用的线程池是ForkJoinPool.commonPool

除此之外,CompletableFuture还支持并行执行
在这里插入图片描述


Disruptor

本篇只是简单的记录一下Disruptor的基本设计,不涉及太深的源码分析

附上一篇美团的技术文章:https://tech.meituan.com/2016/11/18/disruptor.html

Disruptor 核心概念

  • Disruptor(总体执行入口):执行引用。
  • RingBuffer(环形缓冲区):基于数组的内存级别环形数组缓存。
  • Sequence(序号分配器):通过顺序递增的方式,一个Sequence对应一个事件,同时还能消除伪共享。
  • Sequencer(数据传输器):有两个实现类,SingleProducerSequencer(单生产者实现)、MultiProducerSequencer(多生产者实现)。主要作用是实现生产者和消费者之间的并发算法。
  • SequenceBarrier(消费者屏障):用于控制生产者和消费者之间的平衡。
  • WaitStrategy(消费者等待策略):当无可消费事件时的等待策略。(目前数组满需要等待时调用LockSupport.parkNanos(1),不过看注释后续可能会与等待策略挂钩)
  • Event:使用者自定义的事件数据结构。
  • EventHandler:消费者逻辑。
  • EventProcessor:实现了Runnable,并封装了EventHandler,意味着可以线程方式执行消费逻辑。
    在这里插入图片描述

使用案例

        <dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>3.3.4</version></dependency>

在这里插入图片描述

运行流程

1、构造函数

public Disruptor(final EventFactory<T> eventFactory, //事件工厂final int ringBufferSize, //ringBuffer环形数组大小final ThreadFactory threadFactory, //线程工厂final ProducerType producerType, //生产者类型,SINGLE,MULTI两种类型,不同类型有不同的sequencer实现final WaitStrategy waitStrategy) //等待策略{this(RingBuffer.create(producerType, eventFactory, ringBufferSize, waitStrategy),new BasicExecutor(threadFactory));}

其中等待策略有如下几种
在这里插入图片描述

2、disruptor.handleEventsWith()
在这里插入图片描述
这一步就是将消费者逻辑(EventHandler)封装到消费者线程处理器(EventProcessor),并将所有消费线程处理器加入consumerRepository列表。

3、disruptor.start()
在这里插入图片描述
这一步就是启动consumerRepository中的所有消费者线程。

4、消费者线程
以BatchEventProcessor为例
在这里插入图片描述
在这里插入图片描述
消费者线程的逻辑就是不断的循环,从环形数组中获取事件消费,如果没有事件可以获取了就根据不同的等待策略进行等待。

5、disruptor.publishEvent()
在这里插入图片描述
发布一个事件逻辑挺简单的,就是获取一个序号(槽位),然后填充槽位上的事件数据,最后就是发布唤醒等待消费者。

不同生产者模式的区别

一个生产者
一个生产者的情况比较简单

写数据
1、申请写入m个元素
2、判断是否覆盖未消费数据,若无则写入数据

读数据
1、申请读取到序号n
2、从reader cursor开始消费数据到n

多生产者
多个生产者的情况下,为防止多个线程重复写同一个元素。Disruptor的做法是:每个线程获取不同的一段数组空间进行操作。对应的实现方式是,在分配元素的时候,通过CAS判断一下这段空间是否已经分配出去,如果分配了就取下一段。
但是这会遇到一个新问题:如何防止读取的时候,读到还未写的元素。Disruptor的做法是:引入了一个与Ring Buffer大小相同的buffer——available Buffer。当某个位置成功写入时,就把相应位置标记为写入成功。读取的时候,通过遍历available Buffer来获取一段最长的连续已写槽位。

写数据:
1、申请写入m个元素
2、若有m个元素可以写,则返回最大的序号,每个生产者会通过CAS被分配一段独享的空间,各自写入自己的空间
3、标记available Buffer对应位置为成功写入

读数据:
1、申请读取到序号n
2、若此时 write cursor > n,说明这时无法确定连续可读的最大下标。就从reader cursor开始读取available Buffer,一直查到第一个不可用的元素,然后返回最大连续可读元素的位置
3、消费者读取元素

Disruptor设计精髓

1、环形数组的数据结构与初始化时提前分配事件内存,可以实现槽位和事件对象的复用,减少垃圾回收次数
2、递增序号配合长度2次幂的数组长度可通过位运算替换求余
3、缓存行填充解决伪共享问题
4、无锁设计,每个生产者或者消费者会申请一个空间,不同线程在不同空间操作
5、实现了基于事件驱动的生产者消费者模型(观察者模式)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django开发实例总结(入门级、4.2.6、详细)

目录 概述 Django的核心组件包括 Django的项目结构 创建工程&#xff08;4.2.6&#xff09; 实例一&#xff1a;Hello world 实例二&#xff1a;访问一个自定义主页 实例三&#xff1a;通过登录跳转到主页 实例四&#xff1a;主页添加静态文件&#xff0c;包含js、css、…

MVCC(多版本并发控制)

一、什么是MVCC MVCC是为了解决数据库在不加锁的前提下提升并发性和读取效率的一种思想 数据库有已下几种并发情况 读-读&#xff1a;不会产生并发问题读-写&#xff1a;发生隔离性问题&#xff0c;可能导致脏读、幻读、不可重复度写-写&#xff1a;可能存在数据丢失 为了防…

CRM软件助力企业科学决策

我们常说“选择大于努力”&#xff0c;这对于企业发展同样适用。每一家企业管理者在日常工作中都要做大量决策&#xff0c;员工只是将决策落地&#xff0c;而这些决策往往决定了公司大大小小项目实施的顺利与否。因此&#xff0c;采用CRM软件助力企业科学决策显得十分关键。 越…

缓存击穿只会逻辑过期 OR 互斥锁?深入思考 == 鹤立鸡群

网上但凡看得见的文章&#xff0c;大部分在说缓存穿透时都是无脑分布式锁 / 逻辑过期&#xff0c;分布式锁一点问题都没有么&#xff1f;逻辑过期一点问题都没有么&#xff1f;还能不能再进一步优化&#xff1f; 在聊聊缓存击穿的双重判定锁之前&#xff0c;我们将按照循循渐进…

WebSocket协议在java中的应用

文章目录 一、WebSocket介绍1.Http和WebSocket比较&#xff1a;2.应用场景 二、WebSocket使用步骤1.客户端搭建2.导入maven坐标3.导入WebSocket服务端组件WebSocketServer&#xff0c;用于和客户端通信1.ServerEndpoint2.OnOpen3.OnMessage4.OnClose 4.导入配置类WebSocketConf…

【进程】利用 Linux 下的 /proc/pid/ 的内容学习进程

1. 进程号 在计算机中&#xff0c;每一个进程都有一个进程号&#xff0c;进程号类似于一个索引&#xff0c;操作系统就是通过这个进程号快速地找到进程。在 linux 使用 ps -aux 查看进程&#xff0c;可以看到进程号pid&#xff1a; rootswd-Lenovo-G40-80:/proc/4234# ps -au…

设计模式之两阶段终止模式

文章目录 1. 简介 2. 常见思路3. 代码实战 1. 简介 两阶段终止模式&#xff08;Two-Phase Termination Pattern&#xff09;是一种软件设计模式&#xff0c;用于管理线程或进程的生命周期。它包括两个阶段&#xff1a;第一阶段是准备阶段&#xff0c;该阶段用于准备线程或进程…

arcgis删除细长图斑的方法

1、有一张图斑数据如下&#xff1a; 如上图&#xff0c;有很多细长的面要素&#xff0c;需要保留的仅是图中的块状要素。 2、首先要将被合并的要素进行拆分&#xff0c;具体拆分步骤如下&#xff1a; 将所有要素选中&#xff0c;点击高级编辑中的拆分按钮。 3、拆分后图斑就…

汽车贴膜店展示服务预约小程序的作用是什么

很多家庭都有车辆&#xff0c;除了车身自带颜色或外观&#xff0c;部分消费者会选择贴车衣、改色膜以及其它装饰类服务&#xff1b;而市场高需求下也促进了商家生意增长。 但随着线上化程度加深&#xff0c;传统线下门店也面临多重困境&#xff0c;品牌需要线上发展获得生意及…

Sqoop的安装和使用

目录 一.安装 二.导入 1.全量导入 一.MySQL导入HDFS 二.MySQL导入Hive 2.增量导入 一.过滤导入hdfs/hive 二.导出 一.安装 1.下载地址&#xff1a;sqoop下载地址 2.解压 tar -zxvf ./sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C ../module/ 3.改名和配置归属权限 #改名…

SDL Passolo 2022.0.135 Crack

SDL Passolo是一款非常专业的本地化工具。它能够满足软件本地化和游戏行业的特定需求&#xff0c;可以显着加快本地化流程并提高输出质量&#xff0c;简化软件本地化&#xff0c;加快翻译流程&#xff0c;高效翻译图形用户界面&#xff0c;SDL Passolo的是一个特定的软件本地化…

WhatsApp Business为什么会被封号?该如何解决

目前&#xff0c;作为全球即时通讯领域的重要平台之一的WhatsApp已成为企业在营销和与客户沟通时的首选工具。但是长时间、高强度的营销行为很容易导致WhatsApp Business账户突然被封禁&#xff0c;无法再使用账号。即使后续再去进行申诉&#xff0c;要求官方解封该账户&#x…

ARPG----C++学习记录02 Section6位置,偏移,函数

设置actor位置 这一句代码就可以更改位置和旋转 给位置添加偏移offset 将debug的持久都设置为false,在tick中调用&#xff0c;球就会动。这是每帧移动&#xff0c;所以移动速度和帧率有关&#xff0c;需要更改 void Aitem::Tick(float DeltaTime) {Super::Tick(DeltaTime);Ad…

C++设计模式_24_Visitor 访问器

Visitor 访问器也是属于“行为变化”模式。 文章目录 1. 动机( Motivation)2. 代码演示Visitor 访问器3. 模式定义4. 结构(Structure)5. 要点总结6. 其他参考 1. 动机( Motivation) 在软件构建过程中&#xff0c;由于需求的改变&#xff0c;某些类层次结构中常常需要增加新的行…

HR怎么看待PMP证书呢?

作为之前在hr面试过的我来说&#xff0c;这些证书什么的还是很重要的&#xff0c;当你选择一个适合自己的职位时候&#xff0c;大多都是需要看到你的专业能力来进行判断你薪资和适合岗位的依据&#xff0c;pmp证书就是一个很好的衡量证据&#xff0c;而且这个含金量不用了说了非…

centos7 配置搭建 wordpress 博客

环境配置 系统:centos7 CPU:2核 内存:4G 硬盘:40G 一、登录云服务器器 1.单击实例--实例名称 2. 选择安全组页签,单击安全组操作列的管理规则, 3.在入方向添加需要放行的端口。本教程中,在安全组入方向放行SSH默认22端口、Apache默认80端口 4.登录服务器 5.更改主…

读取谷歌地球的kml文件中的经纬度坐标

最近我在B站上传了如何获取研究边界的视频&#xff0c;下面分享一个可以读取kml中经纬度的matlab函数&#xff0c;如此一来就可以获取任意区域的经纬度坐标了。 1.谷歌地球中划分区域 2.matlab读取kml文件 function [sname,lon,lat] kml2xy(ip_kml) % ip_kml ocean_distubu…

高级文本编辑软件 UltraEdit mac中文版介绍说明

UltraEdit mac是一款在Windows系统中非常出名的文本编辑器&#xff0c; UltraEdit for mac对于IT程序猿来说&#xff0c;更是必不可少&#xff0c;可以使用UltraEdit编辑配置文件、查看16进制文件、代码高亮显示等&#xff0c;虽然Mac上已经有了很多优秀的文本编辑器&#xff0…

Mybatis延迟加载(缓存)

延迟加载 分步查询的优点&#xff1a;可以实现延迟加载&#xff0c;但是必须在核心配置文件中设置全局配置信息&#xff1a;lazyLoadingEnabled&#xff1a;延迟加载的全局开关。当开启时&#xff0c;所有关联对象都会延迟加载 aggressiveLazyLoading&#xff1a;当开启时&…