「并发原理专题」AQS 的技术体系之 CLH,java 基础重点知识点
2.同步方法获取链表的尾节点,并将当前节点置为尾节点,此时原来的尾节点为当前节点的前置节点。
3.如果尾节点为空,表示当前节点是第一个节点,直接加锁成功。
如果尾节点不为空,则基于前置节点的锁值(locked==true)进行自旋,直到前置节点的锁值变为 false。
[](
)解锁逻辑
获取当前线程对应的锁节点,如果节点为空或者锁值为 false,则无需解锁,直接返回
同步方法为尾节点赋空值,赋值不成功表示当前节点不是尾节点,则需要将当前节点的 locked=false 解锁节点。如果当前节点是尾节点,则无需为该节点设置。
public class CLHLock {
private final AtomicReference<Node> tail;
private final ThreadLocal<Node> myNode;
private final ThreadLocal<Node> myPred;
public CLHLock() {
tail = new AtomicReference<>(new Node());
myNode = ThreadLocal.withInitial(() -> new Node());
myPred = ThreadLocal.withInitial(() -> null);
}
public void lock(){
Node node = myNode.get();
node.locked = true;
Node pred = tail.getAndSet(node);
myPred.set(pred);
while (pred.locked){}
}
public void unLock(){
Node node = myNode.get();
node.locked=false;
myNode.set(myPred.get());
}
static class Node {
volatile boolean locked = false;
}
}
[](
)MCS 锁
MSC 与 CLH 最大的不同并不是链表是显示还是隐式,而是线程自旋的规则不同:CLH 是在前趋结点的 locked 域
上自旋等待,而 MCS 是在自己的结点的 locked 域上自旋等待。正因为如此,它解决了 CLH 在 NUMA 系统架构中获取 locked 域状态内存过远的问题 。
[](
)MCS 锁具体实现规则:
a. 队列初始化时没有结点,tail=null
b. 线程 A 想要获取锁,将自己置于队尾,由于它是第一个结点,它的 locked 域为 false
c. 线程 B 和 C 相继加入队列,a->next=b,b->next=c,B 和 C 没有获取锁,处于等待状态,所以 locked 域为 true,尾指针指向线程 C 对应的结点
d. 线程 A 释放锁后,顺着它的 next 指针找到了线程 B,并把 B 的 locked 域设置为 false,这一动作会触发线程 B 获取锁。
public class MCSLock {
private final AtomicReference<Node> tail;
private final ThreadLocal<Node> myNode;
public MCSLock() {
tail = new AtomicReference<>();
myNode = ThreadLocal.withInitial(() -> new Node());
}
public void lock() {
Node node = myNode.get();
Node pred = tail.getAndSet(node);
if (pred != null) {
node.locked = true;
pred.next = node;
while (node.locked) {
}
}
}
public void unLock() {
Node node = myNode.get();
if (node.next == null) {
if (tail.compareAndSet(node, null)) {
return;
}
while (node.next == null) {
}
}
node.next.locked = false;
node.next = null;
}
class Node {
评论