咱们知道自旋锁是实现同步的一种方案,它是一种非阻塞锁。它与常规锁的主要区别就在于获取锁失败后的处理方式不一样,常规锁会将线程阻塞并在适当时唤醒它。而自旋锁的核心机制就在自旋两个字,即用自旋操做来替代阻塞操做。某一线程尝试获取某个锁时,若是该锁已经被另外一个线程占用的话,则此线程将不断循环检查该锁是否被释放,而不是让此线程挂起或睡眠。一旦另一个线程释放该锁后,此线程便能得到该锁。自旋是一种忙等待状态,过程当中会一直消耗CPU的时间片。算法
由于在分析CLH锁与MCS锁的缺点时会涉及处理器架构问题,因此在介绍每种自旋锁以前咱们须要先了解两种处理器架构:UMA架构和NUMA架构。在多处理器系统中,根据内存的共享方式能够分为UMA(Uniform Memory Access)和NUMA(Non-uniform Memory Access),即统一内存访问和非统一内存访问。缓存
UMA架构的性质就是每一个CPU核访问主存储的时间都是同样的。下面看基于总线的UMA架构,一共有4个CPU处理器,它们都直接与总线链接,经过总线进行通讯。从这个结构中能够看到,每一个CPU都没有区别,它们平等地访问主存储。访问主存储所需的时间都是同样的,即为统一内存访问。数据结构
当某个CPU想要进行读写操做时,它首先会检查总线是否空闲,只有在空闲状态下才能容许其与主存储进行通讯,不然它将等待直到总线空闲。为了优化这个问题,在每一个CPU的内部引入缓存。这样一来,CPU的读操做就可以在本地的缓存中进行。但这时咱们须要考虑CPU中缓存与主存的数据一致性问题,不然可能会引发脏数据问题。多线程
与UMA架构相反,NUMA架构中并不是每一个CPU对主存储的访问时间都相同的,NUMA架构中CPU能访问全部主存储。经过下图能够看到CPU若是经过本地总线来访问相应的本地主存储的话,则访问时间较短,但若是访问的是非本地主存储(远程主存)则时间将很长,也就是CPU访问本地主存和访问远程主存的速度不相同。NUMA架构的优势就在于,它具备优秀的可扩展性,可以实现超过百个CPU的组合。架构
Craig、Landin、Hagersten三我的发明了CLH锁。其核心思想是:经过必定手段将全部线程对某一共享变量的轮询竞争转化为一个线程队列,且队列中的线程各自轮询本身的本地变量。机器学习
这个转化过程有两个要点:一是应该构建怎样的队列以及如何构建队列?为了保证公平性,咱们构建的将是一个FIFO队列。构建的时候主要经过移动尾部节点tail来实现队列的排队,每一个想获取锁的线程建立一个新节点并经过CAS原子操做将新节点赋给tail,而后让当前线程轮询前一节点的某个状态位。如图能够清晰看到队列结构及自旋操做,这样就成功构建了线程排队队列。二是如何释放队列?执行完线程后只需将当前线程对应的节点状态位置为解锁状态便可,因为下一节点一直在轮询,因此可获取到锁。分布式
因此,CLH锁的核心思想是将众多线程长时间对某资源的竞争,经过有序化这些线程将其转化为只需对本地变量检测。而惟一存在竞争的地方就是在入队列以前对尾节点tail的竞争,但此时竞争的线程数量已经少了不少了。比起全部线程直接对某资源竞争的轮询次数也减小了不少,这也大大节省了CPU缓存同步的消耗,从而大大提高系统性能。性能
CLH锁已经解决了大量线程同时操做同一个变量而带来的同步问题,但它的自旋的对象是前驱节点。在NUMA架构下可能会存在性能问题,由于若是前驱节点和当前节点再也不同一个本地主存储的话则访问时间会很长,这就会致使性能受影响。学习
MCS锁由John Mellor-Crummey和Michael Scott两人发明,它的出现旨在解决CLH锁存在的问题。它也是基于FIFO队列,与CLH锁类似,不一样的地方在于轮询的对象不一样。MCS锁中线程只对本地变量自旋,而前驱节点则负责通知其结束自旋操做。这样的话就减小了CPU缓存与主存储之间的没必要要的同步操做,减小了同步带来的性能损耗。优化
以下图,每一个线程对应着队列中的一个节点。节点内有一个spin变量,表示是否须要旋转。一旦前驱节点使用完锁后,便修改后继节点的spin变量,通知其没必要继续作自旋操做,已成功获取锁。
专一于人工智能、读书与感想、聊聊数学、计算机科学、分布式、机器学习、深度学习、天然语言处理、算法与数据结构、Java深度、Tomcat内核等。