array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 [Java 并发编程] 3. 并发模型 - 爱码网


前言

我们可以通过使用不同的并发模型来实现一个并发系统,线程之间通过协作完成系统给定的任务。不同的并发模型以不同的方式切割任务,线程之间可能存在通信和合作完成不同的任务。


一、并发模型与分布式系统相似

  • 在一个并发系统中,不同的线程之间相互通信。
  • 在一个分布式系统中,不同的进程之间相互通信。

分布式系统比并发系统面临更多的挑战,比如可能面临网络异常、远程主机或进程停止;并发系统理论上也存在相似的问题,比如CPU异常,内存或硬盘损坏等情况,这种异常的概率相对较小,但实际上是存在的。


二、共享状态 vs 隔离状态

2.1 共享状态

共享状态意味着同一系统的多个线程之间共享一些状态(一般指数据,共享一个或多个对象的数据)。当线程共享状态时,容易引发一些并发问题,比如死锁,竞争条件等等,这取决于线程怎样地使用和访问共享数据等等。

[Java 并发编程] 3. 并发模型

2.2 隔离状态

隔离状态意味着同一系统的多个线程之间不共享状态。如果不同的线程之间需要进行通信,可以通过交换不可变对象、或者使用复制的对象进行通信。当多个线程之间不存在共享状态(或共享数据),就可以避免最常见的并发问题。

[Java 并发编程] 3. 并发模型
备注:隔离状态通常情况下可以让代码实现更加简单,并且可以不用担心发生并发访问的问题,避免一些常见的并发问题。在一些特殊情况下,使用分离状态并发设计可能存在更大的挑战。


三、并发模型

3.1 Parallel Workers (并行工作者并发模式)

[Java 并发编程] 3. 并发模型

并行工作者模式:
传入的工作被分配到不同的工作者去执行,每个工作者执行完整的工作流程。工作者在不同的线程中执行。并行工作者并发模型在 Java 应用程序中很常见,JUC包中很多并发工具类的设计都采用了这种并发模型。

优点:

  • 非常容易理解:如果要提高并行效率只需要增加更多的工作者

缺点:

  • 存在共享状态时实现更加复杂
  • 工作顺序不固定

缺点1:存在共享状态时实现更加复杂

并行工作者模式不同线程存在共享数据时,实现变得更加复杂。共享线程通常需要访问共享对象,不论共享对象存在于内存中还是数据库中。如下图所示
[Java 并发编程] 3. 并发模型
某些共享状态是工作队列中的通信机制,也有些共享状态是业务数据、数据缓存、数据库连接池等等。

并行工作者并发模式的线程之间存在共享数据时会更加复杂,当一个线程修改了共享数据变量时,需要确保其他线程读取的共享数据变量是最新的(线程修改了共享数据变量应该把数据保存至主内存中,而不是保存至线程独有的CPU高速缓存栈中)。线程需要避免死锁、竞争条件、或者其他共享状态的并发问题。

当某个线程正在访问共享数据结构时,其他线程会等待正在访问共享数据结构的线程执行完毕,造成阻塞。这可能导致这些线程在竞争共享数据资源,高的竞争条件会导致许多线程访问共享数据时串行执行。

现代非阻塞并发算法可能降低竞争并且提高性能,但是非阻塞并发算法很难实现。

持久性数据结构是另一种选择,当持久性数据结构被修改时,他总是保持修改前版本。当多个线程引用了某个持久性数据结构,其中某个线程对持久性数据结构做了修改时,这个修改了持久性数据结构的线程获得了最新的引用数据,而其他的线程保持原来的引用的数据。在共享数据被修改时,持久性数据结构的方式是一个很好的解决方案。

关于持久性数据结构的举例:

  • 当某个线程给ArrayList添加一个元素时,新添加的元素对其他线程并不可见
  • 也存在某些缺陷,比如LinkedList, 由于LinkedList是链表的数据结构,链表的每个元素都是一个单独的对象,这种数据结构分布在内存块中的各个地方。然而现代CPU在访问顺序的数据结构时要快的多,比如ArrayList,所以CPU在访问ArrayList时能够表现出更加卓越的性能。CPU能够一次加载一个较大的数组类型的数据结构至高速缓存中,但是无法将一个元素分布至内存各个地方的链表(ListedList)加载到CPU的高速缓存中。

无状态工作者
共享数据可以被系统中某个线程修改,其他线程在每次访问共享数据时必须重新读取最新的内存中的数据,以保证线程在执行中时拿到的是最新的内存副本,一个线程不始终保持自己线程独有的CPU高速缓存数据的状态称为无状态。

缺点2:工作顺序不固定
工作A可能被分配给线程A执行,也可能被分配给线程B执行,因此并行工作者模式的执行顺序不是固定的。

3.2 Assembly Line (流水线并发模式)

[Java 并发编程] 3. 并发模型
这种模式就像工厂中流水线的工人一样,每个工作者只负责整个生产车间的一部分工作,每个工作者负责的这部分工作完成之后会传递给下一个工作者。

每个工作者在自己的线程内执行,不存在共享状态,流水线并发模式也被称为无共享并发模式。

流水线并发模式经常在非阻塞IO (NIO) 被设计使用。当一个工作者开始IO操作,其他工作者不需要等待IO操作结束。IO操作很慢,所以等待IO操作完成会浪费大量CPU时间,当IO操作完成时,IO操作的结果会被传递给其他工作者。

关于NIO, 在IO操作确定工人之间的界限。如图

[Java 并发编程] 3. 并发模型
实际上,工作模式可能不只一条流水线,大多数系统可以设计多条流水线。

工作可能并定向到一个工作者进行并行处理。比如一个工作可能同时被分配到工作执行器和日志执行器中执行。

流水线并发模式也被称为反应系统或者事件响应系统。

优点:

  • 不共享状态
  • 可以顺序执行工作

缺点

  • 作业的执行分散在各个工人
  • 调试更加复杂

相关文章: