前情回顾
上一篇文章中主要讨论了自旋锁的特点和其适用场景,然后给出了两种自旋锁的简单实现。
存在的问题
无论是简单的非公平自旋锁还是公平的基于排队的自旋锁,由于执行线程均在同一个共享变量上自旋,申请和释放锁的时候必须对该共享变量进行修改,这将导致所有参与排队自旋锁操作的处理器的缓存变得无效。如果排队自旋锁竞争比较激烈的话,频繁的缓存同步操作会导致繁重的系统总线和内存的流量,从而大大降低了系统整体的性能。
所以,需要有一种办法能够让执行线程不再在同一个共享变量上自旋,避免过高频率的缓存同步操作。于是MCS和CLH锁应运而生。
这两个锁的名称都来源于发明人的名字首字母:
MCS:John Mellor-Crummey and Michael Scott。
CLH:Craig,Landin and Hagersten。
本文先介绍MCS锁的原理和相应实现。
MCS锁
MCS自旋锁是一种基于单向链表的高性能、公平的自旋锁,申请加锁的线程只需要在本地变量上自旋,直接前驱负责通知其结束自旋,从而极大地减少了不必要的处理器缓存同步的次数,降低了总线和内存的开销。
先上实现代码,然后在分析重点:
public class MCSLockV2 {
/**
* MCS锁节点
*/
public static class MCSNodeV2 {
/**
* 后继节点
*/
volatile MCSNodeV2 next;
/**
* 默认状态为等待锁
*/
volatile boolean blocked = true;
}
/**
* 线程到节点的映射
*/
private ThreadLocal<MCSNodeV2> currentThreadNode = new ThreadLocal<>();
/**
* 指向最后一个申请锁的MCSNode
*/
volatile MCSNodeV2 queue;