public class CLHLock {
/**
* 保证原子性 *** 作
*
*/
private AtomicReference<Node> tail = new AtomicReference<>(new Node());
/**
* 每个线程的节点变量不同
*
*/
private ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node());
/**
* 记录前驱节点,并且复用此节点来防止死锁:
* 假设不使用该节点的话,有T1,T2两个线程,T1先lock成功,然后T2调用lock()时会
* 自旋在T1的节点的locked字段上,如果当T1线程unlock()之后,(T2还没获取到CPU时间片),
* T1再次调用lock(),因为此时tail的值是T2线程的节点,其locked值为true,所以T1自旋等待
* T2释放锁,而此时的T2还在等T1释放锁,这就造成了死锁。
*
*/
private ThreadLocal<Node> pred = new ThreadLocal<>(); public void lock() {
Node node = current.get();
node.locked = true;
// 将tail设置为当前线程的节点,并获取到上一个节点,此 *** 作为原子性 *** 作
Node preNode = tail.getAndSet(node);
pred.set(preNode);
// 在前驱节点的locked字段上忙等待
while (preNode.locked); } public void unlock() {
Node node = current.get();
// 将当前线程节点的locked属性设置为false,使下一个节点成功获取锁
node.locked = false;
current.set(pred.get());
} static class Node{
volatile boolean locked;
}
}
注意它的实例变量,tail为一个原子引用,所以在它上的 *** 作都是原子性 *** 作,它是所有线程共享的变量,与后面的两个变量区分开,current是线程本地变量,它的值都和当前线程有关。
current记录的是当前线程的锁情况。
加锁时,现将current的locked属性设置为true,表示当前线程需要获取锁,然后将tail中的值设置为当前线程节点,getAndSet方法设置新值并返回之前的值,这样每个线程节点之间就有一条隐形的”链“关联着,像一个链表。
最后在上一个节点的locked属性上自旋等待。
解锁时,只需把当前节点的locked属性设置为false,这样紧接着的后面一个的线程就会成功的获取锁。
MCS锁:
public class MCSLock {
AtomicReference<Node> tail = new AtomicReference<>();
ThreadLocal<Node> current = ThreadLocal.withInitial(() -> new Node()); public void lock() {
Node node = current.get();
Node pred = tail.getAndSet(node);
// pred的初始值为null,所以第一个加锁线程,直接跳过判断,加锁成功
// tail中记录的是当前线程的节点
if (pred != null) {
pred.next = node;
while (node.locked);
} } public void unlock() {
Node node = current.get();
if (node.next == null) {
// 如果设置成功,说明在此之前没有线程进行lock *** 作,直接return即可;
// 如果失败,则说明在此之前有线程进行lock *** 作,需要自旋等待那个线程将自身节点设置为本线程节点的next,
// 然后进行后面的 *** 作。
if (tail.compareAndSet(node, null))
return;
while (node.next == null);
}
// 通知下一个线程,使下一个线程加锁成功
node.next.locked = false;
// 解锁后需要将节点之间的关联断开,否则会产生内存泄露
node.next = null;
} static class Node{
volatile boolean locked = true;
volatile Node next;
} }
CLH和MCS锁都是自旋锁,公平锁(保证FIFO),独占锁,并且是不可重入锁。
他们两的名字都是发明者的名字的缩写。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)