Java并发-并发模型

可以使用不同的并发模型来实现并发系统。一并发模型指定的系统协作线程如何完成他们给予的任务。不同的并发模型以不同的方式拆分任务,线程可以以不同的方式进行通信和协作。本并发模型教程将更深入地介绍撰写本文时(2015年至2019年)使用的最受欢迎的并发模型。

并发模型和分布式系统的相似性

本文中描述的并发模型类似于分布式系统中使用的不同体系结构。在并发系统中,不同的线程彼此通信。在分布式系统中,不同的进程相互通信(可能在不同的计算机上)。线程和进程本质上非常相似。这就是为什么不同的并发模型通常看起来与不同的分布式系统体系结构相似的原因。

当然,分布式系统还面临着额外的挑战,即网络可能会失败,或者远程计算机或进程关闭等。但是,如果CPU发生故障,网卡发生故障,磁盘发生故障,则在大型服务器上运行的并发系统可能会遇到类似的问题。失败的可能性可能会更低,但理论上仍会发生。

由于并发模型与分布式系统体系结构相似,因此它们经常可以相互借鉴。例如,用于在工作人员(线程)之间分配工作的模型通常类似于分布式系统中的负载平衡模型。错误处理技术(例如日志记录,故障转移,任务的幂等)也是如此。

共享状态与分离状态

并发模型的一个重要方面是,组件和线程是设计为在线程之间共享状态,还是具有独立的状态,这些状态永远不会在线程之间共享。

共享状态意味着系统中的不同线程将在它们之间共享某些状态。通过状态是指一些数据,通常是一个或多个对象或相似。当线程共享状态时,可能会出现争用条件 和死锁等问题。当然,这取决于线程如何使用和访问共享对象。

分开的状态意味着系统中的不同线程在它们之间不共享任何状态。万一不同的线程需要通信,它们可以通过在它们之间交换不可变对象或通过在它们之间发送对象(或数据)的副本来进行通信。因此,当没有两个线程写入同一对象(数据/状态)时,可以避免大多数常见的并发问题。

使用单独的状态并发设计通常可以使代码的某些部分更易于实现和推理,因为您知道只有一个线程将写入给定对象。您不必担心并发访问该对象。但是,使用单独的状态并发性,您可能需要更全面地考虑应用程序设计。我觉得这是值得的。我个人更喜欢单独的状态并发设计。

平行工人

第一个并发模型是我所说的并行工作器模型。传入的工作分配给不同的工人。这是说明并行工作程序并发模型的图:

在并行工人并发模型中,委托人将传入的作业分配给不同的工人。每个工人完成全部工作。这些工作程序并行工作,在不同的线程中运行,并可能在不同的CPU上运行。

如果在汽车制造厂实施并行工人模型,则每辆汽车将由一名工人生产。工人将获得要制造的汽车的规格,并会从头到尾制造所有东西。

并行工作程序并发模型是Java应用程序中最常用的并发模型(尽管正在改变)。java.util.concurrent Java包 中的许多并发实用程序都是设计用于此模型的。您还可以在Java Enterprise Edition应用程序服务器的设计中看到此模型的痕迹。

平行工人优势

并行工作程序并发模型的优点是易于理解。为了增加应用程序的并行化,您只需添加更多工作程序即可。

例如,如果您正在实施Web搜寻器,则可以使用不同数量的工作程序来搜寻一定数量的页面,并查看哪个数字提供了最短的总搜寻时间(意味着最高的性能)。由于Web爬网是一项IO密集型工作,您最终可能会为计算机中的每个CPU /内核使用几个线程。每个CPU一个线程太少了,因为它在等待数据下载时会处于许多空闲状态。

平行工人的劣势

但是,并行工作程序并发模型具有一些隐藏在简单表面下的缺点。我将在以下各节中解释最明显的缺点。

共享状态会变得复杂

实际上,并行工作程序并发模型比上面说明的要复杂一些。共享工作者经常需要访问内存或共享数据库中的某种共享数据。下图显示了如何使并行工作器并发模型复杂化:

这种共享状态中的某些处于诸如工作队列之类的通信机制中。但是这种共享状态中的一些是业务数据,数据缓存,与数据库的连接池等。

一旦共享状态潜入并行工作程序并发模型中,它就会开始变得复杂。线程需要以确保一个线程的更改对其他线程可见的方式访问共享数据(推送到主内存中,而不仅仅是卡在执行该线程的CPU的CPU缓存中)。线程需要避免争用条件, 死锁和许多其他共享状态并发问题。

此外,当线程在访问共享数据结构时互相等待时,并行化的一部分会丢失。许多并发数据结构正在阻塞,这意味着一个或一组有限的线程可以在任何给定时间访问它们。这可能导致对这些共享数据结构的争用。高竞争本质上将导致访问共享数据结构的部分代码的执行序列化。

现代的非阻塞并发算法可以减少争用并提高性能,但是很难实现非阻塞算法。

持久数据结构是另一种选择。永久数据结构在修改后始终保留其自身的先前版本。因此,如果多个线程指向相同的持久数据结构,并且一个线程对其进行了修改,则修改线程将获得对新结构的引用。所有其他线程保留对旧结构的引用,该旧结构仍保持不变,因此是一致的。Scala编程包含几个持久数据结构。

虽然持久性数据结构是对共享数据结构进行并发修改的理想解决方案,但持久性数据结构往往无法很好地执行。

例如,一个持久列表会将所有新元素添加到列表的开头,并返回对新添加元素的引用(该引用随后指向列表的其余部分)。所有其他线程仍保留对列表中先前第一个元素的引用,并且对这些线程而言,列表保持不变。他们看不到新添加的元素。

这样的持久列表被实现为链接列表。不幸的是,链表在现代硬件上的表现不佳。列表中的每个元素都是一个单独的对象,这些对象可以分布在整个计算机的内存中。现代CPU顺序访问数据的速度要快得多,因此在现代硬件上,从阵列顶部实现的列表中可以获得更高的性能。数组顺序存储数据。CPU高速缓存可以一次将更大的阵列块加载到高速缓存中,并让CPU在加载后直接访问CPU高速缓存中的数据。对于链表,将元素分散在整个RAM上,这实际上是不可能的。

无国籍工人

共享状态可以由系统中的其他线程修改。因此,工作人员必须在需要时重新读取该状态,以确保该状态在最新副本上正常工作。无论共享状态是保留在内存中还是外部数据库中,都是如此。不在内部保持状态(但每次需要时都会重新读取状态)的工作程序称为无状态

每次需要时重新读取数据都会变慢。特别是如果状态存储在外部数据库中。

作业排序是不确定的

并行工作程序模型的另一个缺点是作业执行顺序是不确定的。无法保证首先执行或最后执行哪些作业。作业A可以在作业B之前提供给工人,但作业B可以在作业A之前执行。

并行工作程序模型的不确定性使得很难在任何给定的时间点推断系统状态。这也使得很难(如果不是不可能的话)保证一项工作先于另一项工作发生。

流水线

第二种并发模型是我所说的组装线并发模型。我选择该名称只是为了适应早先的“并行工作者”隐喻。其他开发人员根据平台/社区使用其他名称(例如,反应系统或事件驱动的系统)。这是说明组装线并发模型的图:

工人的组织就像工厂中装配线的工人一样。每个工人仅完成全部工作的一部分。完成该部分后,工人会将工作转发给下一个工人。

每个工作程序都在自己的线程中运行,并且不与其他工作程序共享任何状态。有时也称为无共享并发模型。

使用组装线并发模型的系统通常设计为使用非阻塞IO。无阻塞IO意味着当工作人员开始IO操作(例如,从网络连接读取文件或数据)时,工作人员不会等待IO调用完成。IO操作很慢,因此等待IO操作完成会浪费CPU时间。同时,CPU可能正在做其他事情。IO操作完成后,IO操作的结果(例如,读取的数据或写入的数据的状态)将传递给另一个工作程序。

使用非阻塞IO,IO操作将确定工作线程之间的边界。在必须启动IO操作之前,工作人员将尽其所能。然后,它放弃了对工作的控制。IO操作完成后,装配线中的下一个工人将继续工作,直到必须开始IO操作等为止。

实际上,作业可能不会沿着一条装配线流动。由于大多数系统可以执行一项以上的工作,因此工作会根据需要完成的工作在一个工人之间流动。实际上,可能同时存在多个不同的虚拟装配线。这是现实中流水线系统中的工作流的样子:

甚至可以将作业转发给多个工人进行并行处理。例如,可以将作业转发给作业执行者和作业记录器。此图说明了三个装配线如何通过将其作业转发给同一工人(中间装配线中的最后一个工人)来完成:

流水线甚至比这还要复杂。

反应性,事件驱动系统

使用组装线并发模型的系统有时也称为反应式系统或 事件驱动系统。系统的工作人员会对系统中发生的事件做出反应,这些事件是从外界接收到的,或者是其他工作人员发出的。事件的示例可能是传入的HTTP请求,或者某个文件已完成加载到内存等。

在撰写本文时,有许多有趣的反应/事件驱动平台可用,将来还会有更多。一些更受欢迎的似乎是:

  • Vert.x

  • 阿卡

  • Node.JS(JavaScript)

我个人认为Vert.x非常有趣(特别是对于像我这样的Java / JVM恐龙)。

演员与频道

角色和通道是装配线(或反应/事件驱动)模型的两个类似示例。

在演员模型中,每个工人都称为演员。演员可以直接彼此发送消息。消息是异步发送和处理的。如前所述,可以使用Actor来实现一个或多个作业处理装配线。这是说明参与者模型的图:

在渠道模型中,工作人员不直接相互通信。相反,他们在不同的渠道上发布消息(事件)。然后,其他工作人员可以在这些频道上收听消息,而发件人不知道谁在收听。这是说明通道模型的图:

在撰写本文时,渠道模型对我来说似乎更灵活。工人不需要知道稍后在装配线中将处理什么工作的工人。它只需要知道将作业转发到哪个渠道(或将消息发送到等等)。频道上的侦听器可以订阅和取消订阅,而不会影响工作人员对频道的写入。这允许工人之间的联轴器稍松一些。

流水线优势

与并行工作程序模型相比,组装线并发模型具有多个优点。在以下各节中,我将介绍最大的优点。

没有共享状态

工作人员与其他工作人员不共享任何状态的事实意味着无需考虑并发访问共享状态可能引起的所有并发问题,就可以实现他们。这使实施工人变得容易得多。您将工作程序实现为好像是执行该工作的唯一线程-本质上是单线程实现。

有状态的工人

由于工作人员知道没有其他线程修改其数据,因此工作人员可以是有状态的。有状态的意思是他们可以将需要操作的数据保留在内存中,仅将更改写回最终的外部存储系统。因此,有状态工人通常比无状态工人更快。

更好的硬件整合

单线程代码的优势在于,它通常与底层硬件的工作方式更好地相符。首先,当您可以假定代码以单线程模式执行时,通常可以创建更多优化的数据结构和算法。

其次,如上所述,单线程有状态工作者可以在内存中缓存数据。当数据缓存在内存中时,也更有可能将此数据也缓存在执行线程的CPU的CPU缓存中。这样可以更快地访问缓存的数据。

当以自然受益于底层硬件工作方式的方式编写代码时, 我将其称为硬件一致性。一些开发商称这种机械同情。我更喜欢“硬件一致性”一词,因为计算机几乎没有机械零件,并且在这种情况下,“同情”一词被用作“更好地匹配”的隐喻,我相信“符合”一词可以很好地传达。无论如何,这是挑剔的。使用您喜欢的任何术语。

可以订购工作

可以根据组装线并发模型以保证作业排序的方式实现并发系统。作业排序使在任何给定时间点推断系统状态变得更加容易。此外,您可以将所有传入的作业写入日志。然后,在系统的任何部分出现故障的情况下,可以使用此日志从头开始重建系统状态。作业以特定顺序写入日志,并且该顺序成为保证的作业顺序。这是这样的设计的外观:

实施保证的工作订单不一定很容易,但是通常是可能的。如果可以的话,它可以极大地简化备份,还原数据,复制数据等任务,因为所有这些都可以通过日志文件来完成。

组装线的缺点

组装流水线并发模型的主要缺点是,作业的执行通常分散在多个工作人员中,因此也分散在项目中的多个类中。因此,很难确切地知道给定作业正在执行什么代码。

编写代码也可能会更困难。辅助代码有时被编写为回调处理程序。具有许多嵌套回调处理程序的代码可能会导致某些开发人员称之为回调地狱。回调地狱只是意味着很难跟踪所有回调中代码的实际作用,以及确保每个回调都可以访问所需的数据。

使用并行工作程序并发模型,这往往会更容易。您可以打开工作程序代码,并从头到尾阅读几乎执行的代码。当然,并行工作程序代码也可以分布在许多不同的类上,但是执行顺序通常更容易从代码中读取。

功能并行

功能并行是第三种并发模型,最近(2015年)被广泛讨论。

函数并行性的基本思想是使用函数调用实现程序。功能可以看作是相互发送消息的“代理”或“角色”,就像在组装线并发模型(AKA反应或事件驱动系统)中一样。当一个函数调用另一个函数时,这类似于发送消息。

传递给函数的所有参数都将被复制,因此接收函数之外的任何实体都无法操纵数据。该复制对于避免共享数据出现争用情况至关重要。这使得函数执行类似于原子操作。每个函数调用都可以独立于任何其他函数调用执行。

当每个函数调用可以独立执行时,每个函数调用可以在单独的CPU上执行。这就是说,功能上实现的算法可以在多个CPU上并行执行。

使用Java 7,我们获得了java.util.concurrent包含ForkAndJoinPool的软件包,该软件包 可以帮助您实现类似于功能并行性的东西。使用Java 8,我们获得了并行流 ,可以帮助您并行化大型集合的迭代。请记住,有些开发人员对此表示批评ForkAndJoinPool(您可以在本ForkAndJoinPool教程中找到批评的链接)。

关于函数并行性的难点是知道要并行调用哪些函数。跨CPU协调函数调用会带来开销。一个功能完成的工作单元必须具有一定的大小,才能负担此开销。如果函数调用很小,则尝试并行化它们实际上可能比单线程,单CPU执行慢。

根据我的理解(一点都不完美),您可以使用反应性,事件驱动的模型来实现算法,并实现类似于功能并行性的工作分解。使用均匀驱动的模型,您可以更好地控制要并行化的对象和数量(在我看来)。

另外,只有在该任务当前是程序唯一执行的任务时,才有意义地将任务分配给多个CPU,并产生开销。但是,如果系统正在同时执行多个其他任务(例如,Web服务器,数据库服务器和许多其他系统都在执行),则尝试并行化单个任务毫无意义。无论如何,计算机中的其他CPU都将忙于其他任务,因此没有理由尝试以较慢的,功能上并行的任务来打扰它们。组装流水线(反应式)并发模型可能会更好,因为它具有较少的开销(以单线程模式顺序执行),并且与底层硬件的工作方式更好地兼容。

哪种并发模型最好?

那么,哪种并发模型更好?

通常,答案是这取决于系统应该执行的操作。如果您的工作自然是并行的,独立的并且不需要共享状态,则可以使用并行工作器模型来实现系统。

但是,许多工作并非自然而然地平行和独立。对于这些类型的系统,我相信组装线并发模型的优点要大于缺点,比并行工作器模型要有更多的优点。

您甚至不必自己编写所有组装线基础结构的代码。像Vert.x这样的现代平台 已经为您实现了很多功能。我个人将为下一个项目探索在Vert.x等平台上运行的设计。我觉得Java EE不再具有优势。

原文地址:Java并发-并发模型

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

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

相关文章

大模型时代,华为与中软国际携手“打通AI落地应用最后一公里”

文 | 智能相对论 作者 | 叶远风 大模型驱动千行百业智能化变革已经成为广泛共识,下一步的问题是,企业要如何更好地推进? 近日,“2024中关村论坛系列活动——第二届北京人工智能产业创新发展大会”在北京国家会议中心成功举办&a…

yudao-cloud 学习笔记

前端代码 浏览器打开 https://cloud.iocoder.cn/intro/ F12 执行代码 var aaa $(".sidebar-group-items").find("a"); var ll[]; var tt[]; for(var i0;i<aaa.length;i ){ ll.push("https://doc.iocoder.cn" $(aaa[i]).attr("href&quo…

Hi3516DV500+SC2210 AIISP 黑光相机

1. Hi3516DV500 Hi3516DV500是一颗面向行业市场推出的高清智能网络摄像头SoC。该芯片最高支持2路sensor输入&#xff0c;支持最高5M30fps的ISP图像处理能力&#xff0c;支持2F WDR、多级降噪、六轴防抖、多光谱融合等多种传统图像增强和处理算法&#xff0c;支持通过AI算法对输…

第11周,第三期技术动态

大家好&#xff0c;才是真的好。 真没想到&#xff0c;本周是今年第十一周&#xff0c;2024年还有不到三百天就结束了。 今天周五&#xff0c;我们继续介绍与Domino相关产品新闻&#xff0c;以及互联网或其他IT行业动态等。 一、在Windows 10和Windows 11上运行Domino和Trav…

案例研究|辛格林电梯借助DataEase实现数据整合与智能展示

辛格林电梯&#xff08;SIGLEN&#xff09;于2012年创立&#xff0c;是电梯领域的领军品牌之一。该公司总部位于广东佛山&#xff0c;是全国首批获得A1级电梯制造资质的企业&#xff0c;拥有省级工程技术研究中心。辛格林电梯专注于研发和生产高品质电梯产品&#xff0c;涵盖别…

一个用libcurl多线程下载断言错误问题的排查

某数据下载程序&#xff0c;相同版本的代码&#xff0c;在64位系统中运行正常&#xff0c;但在32位系统中概率性出现断言错误。一旦出现&#xff0c;程序无法正常继续&#xff0c;即使重启亦不行。从年前会上领导提出要追到根&#xff0c;跟到底&#xff0c;到年后的今天&#…

通过统一规划和团队整合,提升企业财务洞察

在当今快节奏的商业环境中&#xff0c;企业财务职能部门更应该采取更迅速的行动来适应这个社会。大部分企业期待更高效的战略决策&#xff0c;尤其是面临海量数据信息的堆叠。但是企业领导者应该知道&#xff0c;速度本身并不是最终目标&#xff0c;财务团队必须更快地完成工作…

基于springboot的大学生智能消费记账系统的设计与实现(程序+数据库+文档)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

六种方式,教你在SpringBoot初始化时搞点事情!

前言 在实际工作中总是需要在项目启动时做一些初始化的操作&#xff0c;比如初始化线程池、提前加载好加密证书....... 那么经典问题来了&#xff0c;这也是面试官经常会问到的一个问题&#xff1a;有哪些手段在Spring Boot 项目启动的时候做一些事情&#xff1f; 方法有很多…

火爆新品推荐!AI大模型应用和ai 数字人开发!

火爆新品来袭&#xff01;AI大模型应用和AI数字人开发成为当下科技界的热点话题。随着人工智能技术的不断发展&#xff0c;AI大模型应用已经开始在各个领域大放异彩。从医疗诊断到金融风控&#xff0c;从自然语言处理到智能推荐&#xff0c;AI大模型的应用场景愈发广泛&#xf…

PyCharm 显示无法加载文件 D:\...,因为在此系统上禁止运行脚本。

PyCharm 显示无法加载文件 D:…&#xff0c;因为在此系统上禁止运行脚本。 PyCharm 显示 无法加载文件 D:\python test\AI_CV\venv\Scripts\activate.ps1&#xff0c;因为在此系统上禁止运行脚本。 解决方法&#xff1a; winx点击Windows PowerShell&#xff08;管理员&#…

基于yolov5的SAR舰船检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的SAR舰船检测系统&#xff0c;系统既能够实现图像检测&#xff0c;也可以进行视屏和摄像实时检测_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的SAR舰船检测系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目&…

爬虫实战——巴黎圣母院新闻【内附超详细教程,你上你也行】

文章目录 发现宝藏一、 目标二、简单分析网页1. 寻找所有新闻2. 分析模块、版面和文章 三、爬取新闻1. 爬取模块2. 爬取版面3. 爬取文章 四、完整代码五、效果展示 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不…

【python爬虫】免费爬取网易云音乐完整教程(附带源码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 所属专栏:爬虫实战,零基础、进阶教学 景天的主页:景天科技苑 文章目录 网易云逆向网易云逆向 https://music.163.com/ 下载云音乐 胡广生等,可以选择自己喜欢的歌曲 首先,我们可以先根据…

使用Pytorch导出自定义ONNX算子

在实际部署模型时有时可能会遇到想用的算子无法导出onnx&#xff0c;但实际部署的框架是支持该算子的。此时可以通过自定义onnx算子的方式导出onnx模型&#xff08;注&#xff1a;自定义onnx算子导出onnx模型后是无法使用onnxruntime推理的&#xff09;。下面给出个具体应用中的…

机器学习中的线性代数

基础知识的的复习: 线性代数——深度学习花书第二章 - 知乎 矩阵分解 特征值分解。 PCA(Principal Component Analysis)分解,作用:降维、压缩。 SVD(Singular Value Decomposition)分解,也叫奇异值分解。 矩阵分解的主要应用是:降维、聚类分析、数据预处理、低维度特征学…

Keepalive 解决nginx 的高可用问题

一 说明 keepalived利用 VRRP Script 技术&#xff0c;可以调用外部的辅助脚本进行资源监控&#xff0c;并根据监控的结果实现优先动态调整&#xff0c;从而实现其它应用的高可用性功能 参考配置文件&#xff1a; /usr/share/doc/keepalived/keepalived.conf.vrrp.localche…

三八妇女节智慧花店/自动售花机远程视频智能监控解决方案

一、项目背景 国家统计局发布的2023年中国经济年报显示&#xff0c;全年社会消费品零售总额471495亿元&#xff0c;比上年增长7.2%。我国无人零售整体发展迅速&#xff0c;2014年市场规模约为17亿元。无人零售自助终端设备市场规模超过500亿元&#xff0c;年均复合增长率超50%。…

正则表达式在QT开发中的应用

一.正则表达式在QT开发中的使用&#xff1a; 1.模式匹配与验证&#xff1a;正则表达式最基本的作用就是进行模式匹配&#xff0c;它可以用来查找、识别或验证一个字符串是否符合某个特定的模式。例如&#xff0c;在表单验证中&#xff0c;可以使用正则表达式来检查用户输入的邮…

Agent——记忆模块

在基于大模型的 Agent架构设计方面,论文[1]提出了一个统一的框架,包括Profile模块、Memory模块、Planning模块和Action模块。其中长期记忆的状态维护至关重要,在 OpenAI AI 应用研究主管 Lilian Weng 的博客《基于大模型的 Agent 构成》[2]中,将记忆视为关键的组件之一,下…