并行-基本概念

同步和异步

有人说过这么一个例子:

你去饭馆点菜,点完之后哪也不能去,啥也不能做,直到等菜好了,你才能去做其他事情;这就叫同步。

如果你点了菜,你不用等待,你可以回到座位,刷手机,或者做其他事情;这就叫异步。

不必深入其中,跳出来看看

多个事件,当且仅当只能一个活跃,其余的事件都得伴随(等待),这就是同步。

多个事件,相互之间可以通信,但是不干扰对方状态,这就叫异步。

换句话说,同步是多事件的统一外观表现,而异步更关注个体表现。

好似钟表,只有当秒针转了一圈,分针才能动,分针转了一圈,时针才能动,这就是同步。

在完整事件的表现上,同一时间只能有一个活跃分子。


不过,考虑一下最后一步的情况,当五十九秒的时候,下一秒是分秒齐动。

五十九分五十九秒,三针齐动。这个时刻,同时活跃的是多个子事件,也就是异步了。


有个笑话

王大爷为什么能够一遍刷牙一遍唱歌?

正常情况下,刷牙和唱歌的确是一个同步事件,不能够同时发生。不过

因为王大爷刷的是假牙

我们的观念中,因为两个事件会涉及到资源的独占,也就是嘴巴,两者不能够同时发生。

这样拆离以后,两个事件,就能够同时发生了,也就是异步。


这样的例子还可以举出很多

这样的例子还可以举出很多

两个学生考试,其中一个是学渣,一个是学霸

同步:学渣等学霸写完以后,才能开始"动手"

异步:谁也不看谁的,自己做自己的

public class Student {
    public String answer;
    public void write(String answer){
        this.answer = answer;
    }

    public static void main(String[] args) {
        final Student 学渣 = new Student();
        final Student 学霸 = new Student();
        /**
         *  学渣抄学霸
         */
        学霸.write("小菜一碟");
        学渣.write(学霸.answer);
        /**
         *  每个人自己做
         */
        new Thread(){
            @Override
            public void run() {
                学霸.write("小菜一碟");
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                学渣.write("我不会啊");
            }
        }.start();
    }
}

并发和并行

还是回到王大爷身上,说说并发和并行。

如果王大爷有好牙口,能不能做到一边刷牙一遍唱歌呢?

可以差不多做到:刷一两下,唱一两句

好像有点蠢,不过那是因为我们能够看明白


电脑只有一个CPU,我们也经常是多开的运行几个程序,看起来就是同时运行多个程序

刷一两下,唱一两句,当两件事的切换过程,超过了我们的感知,那看起来就是一起发生的。

  • 并发:多个事情相互切换,交替执行
  • 并行:同时发生多个事情

形象的理解一下

同时跑完十个赛道

并发:一个人,来回切换赛道跑步

并行:十个人,每人一个赛道


在这里,隐含了一个关键的问题:资源,或者叫做执行者。

举个例子,结合前面的同步异步,一起理解一下

做西红柿鸡蛋汤有如下步骤

  1. 切西红柿
  2. 调匀鸡蛋
  3. 做成汤

做汤之前,材料一定要准备好,这是必须的,但是摘菜打蛋之间,存在必然联系么?


材料准备好之前,哪怕油锅烧好了,也得等着,这就是同步。

但是多种材料之间,没有先后,准备好就行,可以同时处理,这就是异步。


一个人,边打蛋,边洗菜,那就是并发了。

两个人,一人打蛋,一人洗菜,这就是并行

分层分类的进行描述

  • 同(异)步:简略的事件关系表达
  • 并(行)发:事件执行区别的描述

关于事件执行,可以参考着三个方面

  • 顺序
  • 执行
  • 资源

同步和异步,是顺序的再整理。

并发和并行,是执行的细划分。

阻塞和非阻塞:是资源相关性的描述。

阻塞和非阻塞

前面说阻塞和非阻塞,和资源相关,那结合一下生活中的场景

  • 上厕所

这个事情的话,男生会比较方便一下,因为有小便池,大号和小号可以隔离开来。

我们来到女厕所,只有一个坑位的女厕所,这时候会发生什么事情?

肯定是堵塞,排队。这就是阻塞。

如果有多个坑位,或者男生有小便池一样,就不会存在这个问题。

  • 挤公交

只来了一辆公交,还挤满了,怎么办?

在这里,需要点明一下容易被忽略的点:存在多个执行者

讨论并发和并行的时候,总说多份资源,这是因为要申明的是多个执行者。

在这里,具体描述的就是资源争抢了。

  • 阻塞:资源问题导致事件执行不下去
  • 非阻塞:不存在资源争抢问题

说起来,关于资源重要的就是两点

  1. 如何解决公共资源的使用问题
  2. 如何保证资源的完整性

对资源问题进行归纳,无非三种

  • 不释放(死锁)

鲁班造了两个绝世的宝盒,除了钥匙,谁也打不开,而且钥匙只有一把。

鲁班分别把另一个盒子的钥匙锁在了另一个盒子里面,然后,死了。

就这样,独占资源以后,不释放,导致后续事件无限等待,无法执行,这就是死锁。

  • 抢不过(饥饿)

挤地铁是个体力活,我面临过两个问题

  1. 上不去
  2. 下不来

人潮实在汹涌。

资源也是一样,优先级高的事件,更为强壮,如果全部是强壮的人,瘦弱的我如何能够上(下)车呢。

资源,对我来说,永远也得不到了。

  • 太礼貌(活锁)

皇上生病了,召见和珅和纪晓岚试药

和珅:纪大人请

纪晓岚:和大人先请

。。。。。

谁都能下手,谁也下不去手;谁都想要,却谁也不能要。

解决策略

饥饿-无饥饿

人人得排队,警察维护治安,不分男女老幼,一律平等对待。

和优先级无关,非公平锁换成公平锁

死锁-无锁

谁都能使用公共资源,不强制隔离,但是需要一定手段保证资源完整。

常用的CAS就是这样去实现锁的功能的。

为了不无限等待,可以设置一下等待次数,或者锁超时时间。

无锁之后也不必礼让,造成活锁了。

两个定律

Amdahl

T 1 T_1 T1:优化前系统耗时

T n T_n Tn n n n个处理器优化后耗时

F F F:串行逻辑占比

T n = T 1 F + T 1 ( 1 − F ) n = T 1 ( F + 1 n ( 1 − F ) ) ⇒ T 1 T n = 1 F + 1 n ( 1 − F ) ⇒ lim ⁡ n → ∞ T 1 T n = 1 F T_n = T_1F + \frac{T_1(1-F)}{n} \\ = T_1(F + \frac{1}{n}(1 - F)) \\ \\ \Rightarrow \frac{T_1}{T_n} = \frac{1}{F + \frac{1}{n}(1 - F)} \\ \Rightarrow \lim\limits_{n \rightarrow \infty}\frac{T_1}{T_n} = \frac{1}{F} Tn=T1F+nT1(1F)=T1(F+n1(1F))TnT1=F+n1(1F)1nlimTnT1=F1

也就是说,如果无法降低串行逻辑的占比,再多的处理器也是无用。

Gustafson

a a a:串行执行时间

b b b:并行执行时间

T 1 = a + n b T_1 = a +nb T1=a+nb

T n = a + b T_n = a + b Tn=a+b

T 1 T n = a + n b a + b = a a + b + n b a + b = F + n ⋅ a + b + a a + b = F + n ( 1 − F ) = n − F ( n − 1 ) ⇒ lim ⁡ n → ∞ T 1 T n = n − F ( n − 1 ) = 0 \frac{T_1}{T_n} = \frac{a + nb}{ a + b} \\ = \frac{a}{a+b} + \frac{nb}{a+b} \\ = F + n \cdot \frac{a + b + a}{a+ b} \\ = F + n( 1 - F) \\ = n - F(n - 1) \\ \Rightarrow \lim \limits_{n \rightarrow \infty}\frac{T_1}{T_n} = n - F(n - 1) = 0 TnT1=a+ba+nb=a+ba+a+bnb=F+na+ba+b+a=F+n(1F)=nF(n1)nlimTnT1=nF(n1)=0

提高n就能无限提高执行速度?

其中的疏漏点在于 T 1 = a + n b T_1 = a + nb T1=a+nb,当 n n n增大后,默认的降低了串行逻辑占比。

如果全是并行逻辑,理论上的确是这样,但是软件的体量不是无限的,而且串行必定存在。

综合来说,并行一定是会提升执行效率的。

JVM性质

为了提高效率,JVM肯定是会做一些优化的,就像前面的西红柿鸡蛋汤

它肯定是并发(行)的去进行打蛋和摘菜,编译过程来看,也就是重排序了。

一般来说,它必须保证如下性质

原子性

原子性说的就是要保证操作的完整性,哪怕只是一部分。

王大爷现在不唱歌了,他想边刷牙边切西瓜。

刷牙时,他要换牙刷。

切瓜时,他要换刀。

如果不能保证原子性,不是西瓜上涂了牙膏,就是把西瓜刀捅到了嘴里。

就是这样,不能保证原子性,没有把牙刷换过来,就要出问题。

可见性

使用的同一份资源,如果对资源的修改不透明,必定会造成误会。

说好一起喝敌敌畏殉情,结果,你拿的却是恒大冰泉,怎么办。

还是王大爷切西瓜,保证了原子性的,的确换了牙刷,但是眼花拿成了刀,认错了资源,哎。

有序性

西红柿鸡蛋汤,你锅烧好了,不等材料准备好,难不成全部放进去?

做个叫花鸡,火好了,鸡都不杀,你这就只是活埋。

可以优化,但是有些事件的强相关顺序还是得保证的。

重排序的话,其他规则如下

  • 保证语义完整
  • volatile:先写后读
  • 锁:先上锁,后解锁
  • 传递:先A后B,先B后C,必定A先于C
  • 线程:start必定先于其他,操作先于终结,中断必定先于执行完毕
  • 对象:构造等操作先于finalize

小结

事件三大件

  • 顺序:事件拆解以后,有些地方存在强耦合顺序关联,但是有些毫无关系
  • 执行:事件的执行者可以存在多个
  • 资源:多个执行者之间公共资源,获取和保证资源完整

对应名词

  • 同(异)步:事件顺序关系
  • 并发(行):执行事件人员
  • (非)阻塞:资源完整问题

JVM处理

  • 原子性:事件的逻辑完整性
  • 可见性:资源的更改一致性
  • 有序性:事件的依赖顺序性

拆解步骤

  • 事件之间需要参与每一步?同步:异步 (事件无挂部分,可以自行完成)
  • 执行者只能有一个?并发:并行 (多个事件同时发生,要么切换着做,要么多个人做)
  • 依赖同一份资源?阻塞:非阻塞 (一人持有,只能排队,无锁情况,也只是能观察,而非直接操作)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值