一、术语问题

术语“并发”,“并行”,“多任务”,“多处理”,“多线程”,”分布式系统“(可能还有其他)在很多编程文献中都以多种相互冲突的方式使用,并且经常被混为一谈。 Brian Goetz 在他 2016 年《从并发到并行》的演讲中指出了这一点,之后提出了合理的二分法:

  • 并发是关于正确有效地控制对共享资源的访问
  • 并行是使用额外的资源来更快的产生结果

“并发”通常表示:不止一个任务正在执行。而“并行”几乎总是代表:不止一个任务同时执行。所以问题就是:“并行”也有不止一个任务正在执行的语义在里面。区别就在于细节:究竟是怎么“执行”的。此外,还有一些场景重叠:为并行编写的程序有时在单处理器上运行,而一些并发编程系统可以利用多处理器。

另一种定义的方式是,在影响到程序的执行性能时:

  • 并发: 同时完成多任务。无需等待当前任务完成即可执行其他任务。“并发”解决了程序因外部控制而无法进一步执行的阻塞问题。最常见的例子就是 I/O 操作,任务必须等待数据输入(在一些例子中也称阻塞)。这个问题常见于 I/O 密集型任务。
  • 并行: 同时在多个位置完成多任务。这解决了所谓的 CPU 密集型问题:将程序分为多部分,在多个处理器上同时处理不同部分来加快程序执行效率。

上面的定义说明了这两个术语令人困惑的原因:两者的核心都是“同时完成多个任务”,不过并行增加了跨多个处理器的分布。更重要的是,它们可以解决不同类型的问题:并行可能对解决 I / O 密集型问题没有任何好处,因为问题不在于程序的整体执行速度,而在于 I/O 阻塞。而尝试在单个处理器上使用并发来解决计算密集型问题也可能是浪费时间。两种方法都试图在更短的时间内完成更多工作,但是它们实现加速的方式有所不同,这取决于问题施加的约束。

这两个概念混合在一起的一个主要原因是包括 Java 在内的许多编程语言使用相同的机制 - 线程来实现并发和并行

  • Java中的Thread类定义了多线程,通过多线程可以实现并发或并行。
  • 在CPU比较繁忙,资源不足的时候(开启了很多进程),操作系统只为一个含有多线程的进程分配仅有的CPU资源,这些线程就会为自己尽量多抢时间片,这就是通过多线程实现并发,线程之间会竞争CPU资源争取执行机会。
  • 在CPU资源比较充足的时候,一个进程内的多线程,可以被分配到不同的CPU资源,这就是通过多线程实现并行。
  • 至于多线程实现的是并发还是并行?上面说的,多线程可能被分配到一个CPU内核中执行,也可能被分配到不同CPU执行,分配过程是操作系统所为,不可人为控制。所以,我们所写的多线程有可能是并发的,也有可能是并行的。
  • 不管并发还是并行,都提高了程序对CPU资源的利用率,最大限度的利用CPU资源。

我们甚至可以尝试以更细的粒度去进行定义(然而这并不是标准化的术语):

  • 纯并发: 仍然在单个 CPU 上运行任务。纯并发系统比顺序系统更快地产生结果,但是它的运行速度不会因为处理器的增加而变得更快。
  • 并发-并行: 使用并发技术,结果程序可以利用更多处理器更快地产生结果。
  • 并行-并发: 使用并行编程技术编写,如果只有一个处理器,结果程序仍然可以运行(Java 8 Streams 就是一个很好的例子)。
  • 纯并行: 除非有多个处理器,否则不会运行。

纯并行和纯并发的图解:

纯并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在围观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

纯并行: 指在同一时刻,有多条指令在多个处理器上同时执行

二、并发的新定义

书中给出的并发定义如下:

并发性是一系列性能技术,专注于减少等待

这实际上是一个相当复杂的表述,可以将其分解:

  • 集合:并发性包含许多不同的方法来解决这个问题。这是使定义并发性如此具有挑战性的问题之一,因为技术差异很大。
  • 性能技术:并发的关键点在于让你的程序运行得更快。在 Java 中,并发是非常棘手和困难的,所以要谨慎使用并发。
  • 减少等待:这个概念很重要而且微妙。无论你运行多少个处理器,你只能在等待发生时使用并发才会产生效益。如果你发起 I/O 请求并立即获得结果,没有延迟,因此无需改进。如果你在多个处理器上运行多个任务,并且每个处理器都以满容量运行,并且没有任务需要等待其他任务,那么尝试提高吞吐量是没有意义的。并发的唯一机会程序的某些部分被迫等待。等待可以以多种形式出现(这解释了为什么存在如此不同的并发方法)。

三、并发能够加快程序运行

3.1 顺序执行与并发执行-为什么并发可以加快程序运行

3.1.1 前趋图

前趋图(DAG,Directed Acyclic Graph)是一个有向无环图,用于描述进程之间执行的先后顺序。图中的每个结点可用来表示一个进程或程序段,乃至一条语句,节点间的有向边则表示两个节点之间存在的偏序关系或前趋关系。前趋关系可用“→\to→”来表示,如果进程PiP_iPi​和PjP_jPj​存在前趋关系,可以写成Pi→PjP_i\to P_jPi​→Pj​,表示PjP_jPj​开始执行前PiP_iPi​必须完成。

在下图中,存在如下的前趋关系:
P1→P2,P1→P3,P1→P4,P2→P5,P3→P5,P4→P6,P4→P7,P5→P8,P6→P8,P7→P9,P8→P9P_1\to P_2,P_1\to P_3,P_1\to P_4,P_2\to P_5,P_3\to P_5,P_4\to P_6,P_4\to P_7,P_5\to P_8,P_6\to P_8,P_7\to P_9,P_8\to P_9P1​→P2​,P1​→P3​,P1​→P4​,P2​→P5​,P3​→P5​,P4​→P6​,P4​→P7​,P5​→P8​,P6​→P8​,P7​→P9​,P8​→P9​

注意前趋图中不允许有循环,否则必然会产生不可能实现的前趋关系。如下所示:

3.1.2 顺序执行

3.1.2.1 程序的顺序执行

通常,一个应用程序由若干个程序段组成,每一个程序段完成特定的功能,它们在执行时,都需要按照某种先后次序顺序执行,仅当前一程序段执行完后,才运行后一程序段。例如,在进行计算时,应先运行输入程序,用于输入用户的程序和数据;然后运行计算程序,对所输入的数据进行计算;最后才是运行打印程序,打印计算结果。用节点表示上述过程,I代表输入操作,C代表计算操作,P代表打印操作,用箭头表示前趋关系。这样,上述的三个程序段就可以用前趋图来表示:

3.1.2.2 程序顺序执行时的特征

  1. 顺序性:指处理机严格地按照程序所规定的顺序执行,即每一操作必须在下一个操作开始前结束
  2. 封闭性:指程序在封闭的环境下运行,即程序运行时独占全机资源,资源的状态(除初始的状态外)只有本程序才能改变它,程序一旦开始执行,其执行结果不受外界因素影响
  3. 可再现性:指只要程序执行时的环境和初始条件相同,当程序重复执行时,不论它是从头到尾不停顿地执行,还是“停停走走”地执行,都可以获得相同的结果。

3.1.3 程序的并发执行

程序顺序执行时,虽然可以给程序员带来方便,但系统资源的利用率却很低。为此,在系统中引入了多道程序技术,使程序或程序段间能并发执行。然而,并非所有的程序都能并发执行。事实上,只有不存在前趋关系的程序间才可能并发执行,否则无法并发执行。

3.1.3.1 程序的并发执行

通过一个常见的例子来说明程序的并发执行,还是以上面的计算程序为例。存在着Ii→Ci→PiI_i\to C_i\to P_iIi​→Ci​→Pi​这样的前趋关系,对每一个作业的输入、计算和打印程序,都必须顺序执行。但若是对一批作业进行处理时,每道作业的输入、计算和打印程序段的执行情况则如下所示:

输入程序(I1)(I_1)(I1​)在输入第一次数据后,由计算程序(C1)(C_1)(C1​)对该数据计算的同时,输入程序(I2)(I_2)(I2​)可以再输入第二次数据,从而使第一个计算程序(C1)(C_1)(C1​)可以与第二个输入程序(I2)(I_2)(I2​)并发执行。事实上,正是由于C1C_1C1​和(I2)(I_2)(I2​)之间并不存在前趋关系,因此它们之间可以并发执行。一般来说,输入程序(Ii+1)(I_{i+1})(Ii+1​)再输入第i+1次数据时,计算程序(Ci)(C_i)(Ci​)可能正在对程序(Ii)(I_i)(Ii​)的第i次输入的数据进行计算,而打印程序(Pi−1)(P_{i-1})(Pi−1​)正在打印程序(Ci−1)(C_{i-1})(Ci−1​) 的计算结果。

从上图可以看出,存在前趋关系:
Ii→Ci,Ii→Ii+1,Ci→Pi,Ci→Ci+1,Pi→Pi+1I_i\to C_i, I_i\to I_{i+1},C_i\to P_i,C_i\to C_{i+1},P_i\to P_{i+1}Ii​→Ci​,Ii​→Ii+1​,Ci​→Pi​,Ci​→Ci+1​,Pi​→Pi+1​
而Ii+1I_{i+1}Ii+1​和(Ci)(C_i)(Ci​)以及(Pi−1)(P_{i-1})(Pi−1​)使重叠的,即在Pi−1P_{i-1}Pi−1​和CiC_iCi​以及Ii+1I_{i+1}Ii+1​之间,不存在前趋关系,可以并发执行。

3.1.3.2 程序并发执行时的特征

  1. 间断性:程序在并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使在这些并发执行的程序之间形成了相互制约的关系。例如在上图中,I、C和P就是三个相互合作的程序当计算程序完成了Ci−1C_{i-1}Ci−1​的计算后,如果输入程序IiI_iIi​尚未完成数据的输入,则计算程序CiC_iCi​就无法进行数据处理,必须暂停运行。只有当程序暂停的因素消失后(如IiI_iIi​已完成数据输入),计算程序CiC_iCi​便可恢复执行。由此可见,相互制约将导致并发程序具有“执行-暂停-执行”这种间断性的活动规律
  2. 失去封闭性:当系统中存在着多个可以并发执行的程序时,系统中的各资源将为它们所共享,而这些资源的状态也有这些程序来改变,致使其中任一程序在运行时,其环境都必然会受到其他程序的影响。例如,当处理机已被分配给某个进程运行时,其他程序必须等待。显然,程序的运行已经失去了封闭性
  3. 不可再现性:程序在并发执行时,由于失去了封闭性,也将导致其又市区可在现性。例如,有两个循环程序A和B,它们共享一个变量N。程序A每执行一次时,都要做N=N+1操作,程序B每次执行时,都要执行print(N)操作,然后执行N=0操作。程序A和B以不同的速度运行,这样就会出现以下的情况:如果N=N+1在print(N)和N=0前,那么N值分别为n+1,n+1,0;如果N=N+1在print(n)和n=0后,那么得到的n值分别为n,0,1;如果N=N+1在print(N)和N=0之间,此时得到的N值分别为n,n+1,0

3.2 并发的实现

实现并发的一种简单方式是使用操作系统级别的进程。与线程不同,进程是在其自己的地址空间中运行的独立程序。进程的优势在于,因为操作系统通常将一个进程与另一个进程隔离,因此它们不会相互干扰,这使得进程编程相对容易。相比之下,线程之间会共享内存和 I/O 等资源,因此编写多线程程序最基本的困难,在于协调不同线程驱动的任务之间对这些资源的使用,以免这些资源同时被多个任务访问。

一些编程语言旨在将并发任务彼此隔离。这些通常被称为函数式语言,其中每个函数调用不产生副作用(不会干扰到其它函数),所以可以作为独立的任务来驱动。Erlang 就是这样一种语言,它包括一个任务与另一个任务进行通信的安全机制。如果发现程序的某一部分必须大量使用并发,并且在尝试构建该部分时遇到了过多的问题,那么可以考虑使用这些专用的并发语言创建程序的这个部分。

Java 采用了更传统的方法,即在顺序语言之上添加对线程的支持而不是在多任务操作系统中分叉外部进程,线程是在表示执行程序的单个进程内创建任务。

并发会带来各种成本,包括复杂性成本,但可以被程序设计、资源平衡和用户便利性方面的改进所抵消。通常,并发性使你能够创建更低耦合的设计;另一方面,你必须特别关注那些使用了并发操作的代码。

并发编程-基础概念介绍相关推荐

  1. python中并发编程基础1

    并发编程基础概念 1.进程. 什么是进程? 正在运行的程序就是进程.程序只是代码. 什么是多道? 多道技术: 1.空间上的复用(内存).将内存分为几个部分,每个部分放入一个程序,这样同一时间在内存中就 ...

  2. Java并发编程基础--ThreadLocal

    Java并发编程基础之ThreadLocal ​ ThreadLocal是一个线程变量,但本质上是一个以ThreadLocal对象为键.任意对象为值的存储结构,这个结构依附在线程上,线程可以根据一个T ...

  3. 区块链教程(二):基础概念介绍

    注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 本系列重点在于以太坊基础知识.以太坊客户端以及以太坊solidity编程,因此博客重点在于以太坊核心知识点的掌握,区块链部分的基础知识可以作为补充 ...

  4. Java并发编程的艺术-Java并发编程基础

    第4章 Java并发编程基础 ​ Java从诞生开始就明智地选择了内置对多线程的支持,这使得Java语言相比同一时期的其他语言具有明显的优势.线程作为操作系统调度的最小单元,多个线程能够同时执行,这将 ...

  5. 并发的基础概念以及优缺点

    并发的基础概念以及优缺点 一. 为什么要有并发 二. 并发与并行的区别 三. 并发编程的优点 1. 充分地利用多核 CPU 的好处 2. 提高系统的响应速度 四. 并发编程的缺点 1. 上下文切换 2 ...

  6. python 舍去小数_零基础小白Python入门必看——编程基础概念

    1. 程序的构成 程序由模块组成,一个模块对应python的源文件 ,一般后缀为:.py 模块由语句构成 语句是python程序的构造单元,用于创建对象.变量赋值.调用函数.控制语句等. 2. 对象 ...

  7. 【牛客网】-【并发详解】-【并发编程基础】-【原子类】

    目录 并发编程基础 原子类 参考书目: 并发编程基础 在操作系统中,并发是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处 ...

  8. 物流仓储管理系统(wms)基础概念介绍[临沂艾思app开发]

    物流仓储管理系统(wms)基础概念介绍: 物流仓储管理系统(wms)是一套基于追溯系统成品的多级渠道仓库管理流程,能完成从工厂到终端的多级仓库物流的管理及产品全生命周期流通的追溯管理,并以产品追溯码为 ...

  9. Python笔记002-Python编程基础概念

    第二章(1):Python编程基础概念 1. Python 程序的构成 Python 程序有模块组成.一个模块对应 Python 源文件,一般后缀名是:.py. 模块有语句组成.运行 Python程序 ...

  10. 自己动手实现蓝牙MESH应用系列 | 第一篇:蓝牙MESH基础概念介绍

    文章目录 1. 前言 2. 概述 2.1. 蓝牙风格(Flavors) 2.2. mesh网络的动机 2.3. mesh网络中的消息传输方式 2.3.1. 以消息为中心的通信 - 发布/订阅(publ ...

最新文章

  1. [原]执行存储过程后返回影响的行数
  2. python语言入门m-Python入门基础三-函数
  3. (数据库系统概论|王珊)第三章关系数据库标准语言SQL-第四节:数据查询
  4. 【计算机视觉】opencv姿态解算6 理论算法调研 PNP问题 5种算法
  5. jenkins没有参数化构建过程选项
  6. 跨浏览器javascript
  7. 静态GPS控制测量使用技术方法
  8. 基于eclipse的android项目实战—博学谷(一)欢迎界面
  9. 正弦函数的频谱图matlab,怎样用MATLAB画正弦函数以及怎样看频谱图?
  10. 用 Python 进行 OCR 图像识别
  11. 51单片机学习历程——建立新的工程
  12. Android | Tangram动态页面之路(七)硬核的Virtualview
  13. 缺省的linux系统中,linux缺省的文件系统是
  14. 戴尔通过F12一次性引导菜单刷新BIOS
  15. MFC中改变按钮颜色的方法
  16. 充电桩软件设计之RTOS 系统选择
  17. SAP重置公司代码资产会计(FI-AA)数据-OABL
  18. 金岩石:陈晓失误已铸成大错
  19. 开发者工具 Top 100 榜单出炉啦!
  20. 2020中南大学研究生招生夏令营机试题题解

热门文章

  1. 利用计算机模拟专家给病人,《计算机应用基础2007》复习资料
  2. 继续南山聊代码!Apache Kafka × Apache Flink Meetup · 深圳站
  3. SQL 中的 in 与 not in、exists 与 not exists 的区别以及性能分析
  4. 谷歌开源内部代码评审规范
  5. 厉害了!为了干掉 HTTP ,Spring团队又开源 nohttp 项目!
  6. xpath的基础知识
  7. php使用框架优缺点,PHP四大主流框架的优缺点总结(上)
  8. html5页面签字,html5 canvas实现的手机端签字板
  9. @configuration注解_SpringBoot注解大全,收藏一波!!!
  10. mysql字段分隔符拆分_MySQL——字符串拆分(含分隔符的字符串截取)