CLH Lock摘要
CLH lock is Craig, Landin, and Hagersten (CLH) locks, CLH lock is a spin lock, can ensure no hunger, provide fairness first come first service.
The CLH lock is a scalable, high performance, fairness and spin lock based on the list, the application thread spin only on a local variable, it constantly polling the precursor state, if it is found that the pre release lock end spin.
CLH锁是自旋锁的一种,对它的研究是因为AQS源代码中使用了CLH锁的一个变种,为了更好的理解AQS中使用锁的思想,所以决定先好好理解CLH锁
Java代码实现
public class ClhSpinLock implements Lock{
private final ThreadLocal<Node> prev;
private final ThreadLocal<Node> node;
private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node());
public ClhSpinLock() {
this.node = new ThreadLocal<Node>() {
protected Node initialValue() {
return new Node();
}
};
this.prev = new ThreadLocal<Node>() {
protected Node initialValue() {
return null;
}
};
}
/**
* 1.初始状态 tail指向一个node(head)节点
* +------+
* | head | <---- tail
* +------+
*
* 2.lock-thread加入等待队列: tail指向新的Node,同时Prev指向tail之前指向的节点
* +----------+
* | Thread-A |
* | := Node | <---- tail
* | := Prev | -----> +------+
* +----------+ | head |
* +------+
*
* +----------+ +----------+
* | Thread-B | | Thread-A |
* tail ----> | := Node | --> | := Node |
* | := Prev | ----| | := Prev | -----> +------+
* +----------+ +----------+ | head |
* +------+
* 3.寻找当前node的prev-node然后开始自旋
*
*/
public void lock() {
final Node node = this.node.get();
node.locked = true;
Node pred = this.tail.getAndSet(node);
this.prev.set(pred);
// 自旋
while (pred.locked);
}
public void unlock() {
final Node node = this.node.get();
node.locked = false;
this.node.set(this.prev.get());
}
@Override
public void lockInterruptibly() throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public boolean tryLock() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// TODO Auto-generated method stub
return false;
}
@Override
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
}
private static class Node {
private volatile boolean locked;
}
}
简单的看一下CLH的算法定义
the list, the application thread spin only on a local variable, it constantly polling the precursor state, if it is found that the pre release lock end spin.
基于list,线程仅在一个局部变量上自旋,它不断轮询前一个节点状态,如果发现前一个节点释放锁结束.
所以在java中使用了ThreadLocal作为具体实现,AtomicReference为了消除多个线程并发对tail引用Node的影响,核心方法lock()中分为3个步骤去实现
-
初始状态 tail指向一个node(head)节点
private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node());
-
thread加入等待队列: tail指向新的Node,同时Prev指向tail之前指向的节点,在java代码中使用了getAndSet即CAS操作使用
Node pred = this.tail.getAndSet(node); this.prev.set(pred);
-
寻找当前线程对应的node的前驱node然后开始自旋前驱node的status判断是否可以获取lock
while (pred.locked);
同理unlock()方法,获取当前线程的node,设置lock status,将当前node指向前驱node(这样操作tail指向的就是前驱node等同于出队操作).至此CLH Lock的过程就结束了
测试ClhSpinLock
public class LockTest {
static int count = 0;
public static void testLock(Lock lock) {
try {
lock.lock();
for (int i = 0; i < 10000000; i++) ++count;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
final ClhSpinLock clh = new ClhSpinLock();
final CyclicBarrier cb = new CyclicBarrier(10, new Runnable() {
@Override
public void run() {
System.out.println(count);
}
});
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
testLock(clh);
// 这段代码是非lock比较使用
// for (int i = 0; i < 10000000; i++)
// count++;
try {
cb.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
测试代码使用了CyclicBarrier辅助当所有子线程完成后输出static变量count的值
结果发现输出的值和预期一样,CLH Lock完成了独占式锁的功能
小结
CLH Lock是一种比较简单的自旋锁算法之一,因为锁的CAS操作涉及到了硬件的锁定(锁总线或者是锁内存)所以性能和CPU架构也密不可分,该兴趣的同学可以继续深入研究包括MCS锁等。CLH Lock是独占式锁的一种,并且是不可重入的锁,这篇文章是对AQS锁源代码分析的预热篇
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。