1 .CLH锁
CLH是一种基于单向链表的高性能、公平的自旋锁。申请加锁的线程通过前驱节点的变量进行自旋。在前置节点解锁后,当前节点会结束自旋,并进行加锁。在SMP架构下,CLH更具有优势。
锁值:我把自旋条件定义为锁值 locked。locked == true 表示节点的处于加锁状态或者等待加锁状态,locked == false 表示节点处于解锁状态
1. 基于线程当前节点的前置节点的锁值(locked)进行自旋,locked == true 自旋,locked == false 加锁成功。
2. locked == true 表示节点处于加锁状态或者等待加锁状态。
3. locked == false 表示节点处于解锁状态。
4. 每个节点在解锁时更新自己的锁值(locked),在这一时刻,该节点的后置节点会结束自旋,并进行加锁。
1.1 加锁逻辑
获取当前线程的锁节点,如果为空则进行初始化。
通过同步方法获取尾节点,并将当前节点置为尾节点,此时获取到的尾节点为当前节点的前驱节点。
如果尾节点为空,则表示当前节点为第一个节点,加锁成功。
如果尾节点不为空,则基于前驱节点的锁值(locked==true)进行自旋,直到前驱节点的锁值 locked == false。
1.2 解锁逻辑
获取当前线程的锁节点,如果节点为空或者锁值(locked== false)则无需解锁,直接返回。
使用同步方法为尾节点赋空值,赋值不成功则表示当前节点不是尾节点,需要将当前节点的 locked == false 已保证解锁该节点。如果当前节点为尾节点,则无需设置该节点的锁值。因为该节点没有后置节点,即使设置了,也没有实际意义。
1.3 Java代码
package org.learn.lock;
import java.util.concurrent.atomic.AtomicReference;
/**
* MCS:John Mellor-Crummey and Michael Scott
* CLH:Craig,Landin and Hagersten
* @author zhibo
* @version 1.0
* @date 2018/11/7 10:39
*/
public class CLHLock implements Lock {
private AtomicReference<CLHNode> tail;
private ThreadLocal<CLHNode> threadLocal;
public CLHLock() {
this.tail = new AtomicReference<>();
this.threadLocal = new ThreadLocal<>();
}
@Override
public void lock() {
CLHNode curNode = threadLocal.get();
if(curNode == null){
curNode = new CLHNode();
threadLocal.set(curNode);
}
CLHNode predNode = tail.getAndSet(curNode);
if(predNode != null){
while (predNode.getLocked()){
}
}
}
@Override
public void unlock() {
CLHNode curNode = threadLocal.get();
threadLocal.remove();
if(curNode == null || curNode.getLocked() == false){
return;
}
if(!tail.compareAndSet(curNode, null)){
curNode.setLocked(false);
}
}
public static void main(String[] args) {
final Lock clhLock = new CLHLock();
for (int i = 0; i < 10; i++) {
new Thread(new DemoTask(clhLock, i + "")).start();
}
}
class CLHNode {
private volatile boolean locked = true;
public boolean getLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
}
}