目录
什么是并发
首先需要知道什么是并发?
case:淘宝电商刚起步时,大多都是一个人卖东西,打包、发货。随着电商产业的蓬勃发展,订货单量大幅提升,一个人工作能力有限,会导致发货不及时的情况出现,从而降低消费者的好感度,退单情况严重。为解决这种情况呢,招聘2人进行打包、发货,提高发货量,保证发货时间。
这就是最基本的并发了,每个人都可以看成是一个线程,同样的工作量,干得人多了自然就快了。
从本质上来说,为什么会出现并发的概念,无疑就是为了缩短执行时间、提高执行效率,使用多个执行器同时处理同一个大任务的过程。
数据竞争
可是好景不长,周末盘货时,你发现货少了很多。这办公室里也没遭贼,怎么就会少货呢?细细一查快递单,你发现竟然有几单发重了。之后的几天你都细细留意了一下发货的过程,最后发现是因为每个人都会拿着一张发货清单去备货,如果有一些订单不小心打印重复了,就有可能会被不同的人重复发货。虽然数量不多,但是也很心痛啊。这个问题产生的原因就是因为每个人在备货之前拿到的订单状态(未发货)在实际备货时发生了变化(已由其他人发货)。这种对一个共享数据(订单的发货状态)本应独占的读取、检查、修改过程,如果发生了并发,这种情况就被称为
数据竞争
。而这个读取、检查、修改的过程就被称为临界区
,临界区
指的就是一个存在数据竞争
的代码片段。
为什么会出现数据竞争?
根本原因:一个数据本来应该只能由一个执行器完整地执行读取、检查、修改过程,但是如果出现了并发,那么就没办法保证到了“修改”这一步时的数据还保持了“读取”时的值了。
确定原因后,有人想到了一个好办法,可以打印一张总的发货清单,这样所有人都必须以这个清单上的订单是否发货来确定是否要对订单进行备货并发货了。因为清单只有一份,所以每次只能由一个人来修改订单的发货状态。这种只能由一个执行器进行数据修改操作来避免发生
数据竞争
问题的做法就被称为互斥
,也就是我们常说的锁
了。
分布式并发概念
分布式
因为你管理得当,生意发展得很快,现在的办公室里已经堆不下所有衣服了。所以你又租了一个仓库来同样进行发货。两个地方都会进行发货,那么就可以把每一个仓库理解为一台独立的计算机,这样通过多台计算机完成同一任务的方式就可以被称为
分布式
,这样的一组计算机的集合就被称为集群
。
但是
这时候之前用一张纸质的总发货清单的
数据竞争
解决方式就行不通了,所以我们需要把这张总发货清单放到云端,让大家可以通过网络进行编辑,但是每次只能一个人编辑。在这种情况下,我们可以把两个仓库各自看成一台计算机/进程,而每个仓库里的人就是这个进程中的线程。这样的话这张总发货清单就成为了一个分布式锁
,因为它每次只能有一个人编辑,所以是一个互斥锁
,或者简称为锁
;而因为它可以被两个进程/计算机(仓库)共同使用,所以被称为是分布式锁
。
什么是进程/线程?
可以简单地将进程理解为我们电脑/手机上的一个应用,同一台手机上的每个App都是一个进程,同一个App在每个手机上也是一个进程。进程和进程之间可以理解为是两个仓库,互相之间物理隔离;而线程就是仓库里的每一个人,他们共享同一个办公空间。这里的办公空间就可以理解为操作系统中的虚拟内存空间。
进程包含线程。
分布式数据不一致
因为生意比较好,所以所有人都很忙。有时候就会因为有一些人虽然在云端表格上已经勾上了一个订单,但是一忙就给忙忘了。其他人怕重复发货又不会再去处理已经勾上的订单了,因为这样导致的未发货订单让店铺被投诉了好多次,影响非常大。这种在并发过程中修改了数据状态但是没有完成后续执行的情况就会出现
数据不一致
,即订单已经被勾上,但实际并没有发货。但是作为聪明的老板,你又想到了解决的方法。每隔一小时两个仓库就会各派一个人检查一下已经勾上的订单是否已经都打包完贴上快递单了。这种每隔一段时间就检查并处理遗漏的
数据不一致
订单的任务就被称为兜底任务
。而通过兜底任务
实现的在最后所有订单都会达到数据一致状态的情况就被称为最终一致性
。
优化方式
大家可能早就觉得前面介绍的总发货清单的方法太傻了,只要每个订单都只打印一张发货清单,由单独一个人去负责分发清单就可以了,其他人只要处理好自己被分配到的订单就可以了。最后再加上一个兜底任务对订单的发货情况进行二次校验基本上就不会发生漏发或者重发的情况了。这种由一个执行器进行任务拆分,再由一组执行器进行处理,最后再由一个或一组执行器进行结果汇总的处理方式就是现在非常流行的
map-reduce
方法了。这种方法在大数据或者程序语言标准库里都有大量的应用,比如大数据领域赫赫有名的Hadoop和Java语言中的ForkJoinPool
都使用了这种思想。