基本概念
并行和并发的区别
并行(parallel)
同时做某些事情,可以互不干扰的同时做几件事。
举例
:高速公路的车道,双向4车道,所有车辆(数据)可以互不干扰的在自己的车道上奔跑 (传输)。
在同一个时刻,每条车道上可能同时有车辆在跑,是同时发生的概念。
如下图所示,在t时刻可以有多个程序在执行中,这样就称作并行。(注意是同一时刻,不是同一个时间段)
并发,(concurrency)
也是同时做某些事,但是强调,同一个时段做几件事
乡村公路一条车道,半幅路面出现了坑,交警指挥车辆走另外半幅路面,一个方向放行3分钟,
停止方向,换另一个方向放行。发生了车辆同时要通过路面的事件,这就是并发。
如下图所示,在T时间段内可以有多个程序在执行中,这样就称作并发。(注意是同一时间段,一般这个时间段都指的是一个比较小的时间段,不是同一个时刻)
经典的生产者和消费者模型
比如有这样一个生产者:一个卖煎饼果子的老奶奶,在没有人买的时候可多做出几个让后放在架子上,但是架子上的容量是有限的,比如最多能放10个,当老奶奶想空余的时间多做出几个放在架子上的时候每次都要看一看架子上有没有空余的空间来放做出来的煎饼果子。
比如有这样的消费者:很多喜欢吃煎饼果子的小姐姐,都喜欢到老奶奶这里来买煎饼果子,如果小姐姐来的早,在架子上有很多已经做好了的煎饼果子,然后小姐姐只要一来,看一看架子上是否有做好了的煎饼果子,如果有就直接就买上走人了,但是晚来的小哥哥就没有那么运气好了,当他来的时候架子上没有了,只有等待着老奶奶做好了再卖给他。
并发的解决
举例“食堂打饭”:
很多人都肯定经历过,中午12点,下课后一望无际的脑袋都涌向食堂,(一小撮吃外卖的不算,并不是很多人家里都富有,我也是只有吃食堂的份了)。特别是在12:00--12:30这个时间段人流量是最大的,这就叫做高并发。为了使得食堂不会因为我们人数太多了供应不过来,崩掉了。所以食堂就要求我们排队,这就相当于我们程序的消息队列了,使用队列的方式是一个可以解决并发的一种方法,虽然这种方法不一定很好,但是这是解决并发的一种方法。
所以第一种解决办法就是:队列、缓冲区
:
其实在食堂的打饭窗口前都有大片用来排队的区域,这样一个容纳学生的区域就叫做缓冲区,也就是在程序中暂时存放数据的地方。
缓冲区技术最开始提出来是用在处理内存和CPU的速度问题的,大家都知道内存读写很快,但是我们CPU的计算能力更是日益更加的强大,就出现了,内存的读写数据跟不上CPU的处理数据,然后人们就发明了cache(缓存),CPU在处理数据的时候先把数据放入IO数据更快的cache中,然后再进行处理,也可以理解成,cache就是一个数据临时存放的地方,一般的计算机中都有多级缓存,CPU中一般也有cache,CPU中的cache一般称作片内cahe。
优先队列
假设今天是女神节,学校为了给学校的女神们过节日,然后就出现了这样一个规则,只要有女生来打饭就直接去最前面,不用跟在男生后面进行排队。这样的一个队列,在编程中就叫做优先队列。
但是作为一个理工科大学,女生还是寥寥无几。
这种优先队列的方式还是不太好,还有什么其他的方法呢?
我们可以这样思考,比如我们去食堂打饭,然后不排队了,直接就往里面冲,但是我们注意的是总有人先去到窗口里面,每个打饭的阿姨都只能给一个打饭的同学服务。也可以叫做这个同学把这样阿姨占用了,在我们编程里面,当你去请求一个资源的时候,如果你动作够快,然后占用了这个资源,然后就把这个资源给锁上了。
所以第二种解决办法就是:争抢、锁资源
:
但是这样方法也不是很好,比如:有的同学为了抢着先打饭,甚至争的鼻青脸肿的。也比如说某个中学占用了一个打饭的阿姨,但是他在哪里想了一半天都没有想好吃什么,后面判断的同学就一直在后面等着,最后都争吵起来了。还有一种情况就是比如某个同学平时也不锻炼,身体素质很差,更本就抢不赢其他的同学。(在Linux内核中其实也是这种争抢机制,比如我们人类的社会,如果你太弱就抢不赢别人,最后物竞天择……,所以我们要努力学习,增强自己。)
虽然争抢式的方式有很多的问题,但是我们也不能只看缺点,同时争抢式也有优点,比如:比如可以激励我们的同学节省打饭的时间。
这学期我们学校的一个食堂就升级了,以前是同学们走到打饭的窗口前面,要吃什么然后再叫阿姨打,但是这样效率并不是很高。所以这学期就换成了这样的机制,阿姨们提前把饭和菜提前用小点的碗和盘子盛好,然后同学们走到窗口前面就只要看着自己喜欢的菜和饭,端着就走了,到另外的地方买单就好。
所以第三种解决办法就是:预处理
:
预处理的核心思想就是提前准备好用户请求的资源。这不失为一个好的办法。
另外比如学校因为招到了很多的新生,食堂常常是人满为患,所以学校就准备扩建了。另外又修了一栋楼来开食堂。导致打饭的窗口也变多了,但是这样花费就大了,如果学校没有这样的积蓄是盖不起大楼的。
所以第四种解决办法就是:并行
:
比如我们最以前的8086芯片就只是单核心的,现在都是多核心的,处理的速度自然就提升了上来,比如打饭的窗口多了,同学们打到饭的速度也就变快了。
后面又接着分析,食堂的阿姨因为工作不积极,或者说是业务能力不行,打饭的动作太慢了,出现了这种情况怎么办呢,也就有解雇,然后招聘能力更好的打饭的阿姨了。
所以第五种解决办法就是:提速
:
这种提速就是更换新的东西,比如我们电脑运行速度越来越卡了,所以也就只用换更好的CPU,速度更快的内存。但是这种提升比较不常用,比如:我们现在就算你高薪请一个能力很强的阿姨但是,人重视要累的,所以这种提升是有上限的。
我们应该在坐飞机或者坐火车的时候,常常在安检的时候地方前面有个很长的用铁栏杆为其他的走廊,我们所有要过安检的人先从这个走廊中依次排队进入,当排完队后,后面就可以进入到各个安检口,检查身体和行李了。
所以第六种解决办法就是:消息中间件
:
常见的消息中间件有RabbitMQ、ActiveMQ ( Apache )、RocketMQ (阿里Apache )、kafka ( Apache )等。
当然还有其他手段解决并发问题,但是已经列举除了最常用的解决方案,一般来说不同的并发场景用不同的策略,而策略可能是多种方式的优化组合。
可能有的人无法区分上面我讲的,消息队列和消息中间件,这两种方法的区别,其实这两种本质上都是消息队列的机制,只是前一种是在我服务的内部实现,二后一种是在服务的外部实现