『阿男教你玩转Concurrent Programming』*学习的脉络(2)*

『阿男教你玩转Concurrent Programming』*学习的脉络(2)*

为了解决协调并发执行循序协调资源的使用这两个问题,人类做了大量的研究,产生了很多的研究结果,制造了很多解决实际问题的工具。而我们要学习的就是这些东西了。

为了协调并发执行顺序和协调资源的使用,我们需要协调的手段。比如,如何让Worker A等待Worker B完成了特定任务后,再继续执行任务?

还比如,如果Worker AWorker B都要使用Resource X,但是不能同时对Resource X做操作,怎样才能让它们不同时操作Resource X

这种情况下,我们往往使用来实现互斥独占,来完成协调执行顺序和只允许一个worker访问某个resource的工作。

于是,,也就是lock的实现,如何可靠地获得锁,如何可靠地释放锁,就是一个研究领域。

什么叫可靠地获得锁?我们可以自己想想。应该可以大概想到一些内容,比如:不能让两个worker同时获得同一个lock,那么这个lock也就没有意义了。所以在lock的实现方面,我们不能让这种事情发生。

为了避免这种事情发生,最终我在硬件上有保障,保证lock的可靠性,也就是说,硬件必须要提供atomicity,原子性。体现在观测结果上,所有worker都需要观测到一个object状态改变一致性。不能说A观测到一个状态的改变,B没观测到,如果是这样的话,锁的实现是无从谈起的。

大家是不是觉得概念上很抽象?但是在软件的实现上,我们经常会和上面所说的atomicity打交道的。

最常见的就是Java里面的volatile这个关键字。如果我们一个变量不使用volatile这个关键字,那么Java的虚拟机可能就不会同步这个变量。如果你的程序是multi-threaded,也就是多线程的,每一个thread里面观测到的这个变量的值可能是不一样的。

为什么?因为层次的丰富性。什么意思?我们的程序面对一个多层的世界:虚拟机,操作系统,CPU。

现代的虚拟机或是操作系统使用的是复杂的虚拟内存实现,为的是效率最大化。对于多线程,使用的是allocation on demanding的策略。也就是说,能共享的数据,就尽量共享,需要分割的数据,再独立划分空间。

然后是CPU,现代的CPU都提供缓存,提高运行速度,多核CPU下,每颗CPU有自己独立的缓存。你看到的数据是CPU缓存数据,而不是内存里面的实际数据。

于是什么时候CPU更新缓存就成了问题。单线程的程序没有这个焦虑,但是多线程的程序里面,就算是一个Thread更新了一个共享变量的数据,另一个Thread也不一定能看得到这个改变。

为什么?这里面有好多层要考虑的因素:首先,CPU的缓存也是提供给操作系统指令接口,由操作系统负责更新策略的。

其次,虚拟机在实现的时候,要提供给代码编写者接口控制CPU的缓存更新。比如Java的volatile关键字。一切为了性能。

但是对于新手,就不知道这些,可能觉得,我在一个thread里面更新了一个共享数据的值,那么在另一个thread里面就可以观测到,其实根本不是。

如果你不使用volatile关键字,另一个thread读到的可能还是另一个CPU里面的缓存数据,而不是内存里面更新后的数据。下图展示了CPU缓存,Thread观测到的数据,以及内存的关系[^1]:

[^1] http://tutorials.jenkov.com/java-concurrency/volatile.html

输入图片说明

所以,lock的实现,要基于所有这些层次丰富的世界进行考量,要考虑到所有的观测不一致的可能性。所以说,软件的的实现,是基于硬件操作系统虚拟机一个综合考虑,最终落到一个实处:CPU是一个晶振芯片,时间的原子性。

这次阿男给大家讲了atomicitylock,下次我们再讲基于这两点之上的领域和设计。

转载于:https://my.oschina.net/u/3195023/blog/836067

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值