自旋锁是为了减少线程挂起次数;
而轻量级锁是在加锁的时候,如何使用一种更高效的方式来加锁。
1.先解释自旋锁:
未使用自旋锁的线程状态:运行 -》 阻塞 -》 运行 被挂起
被唤醒使用自旋锁线程状态:运行 -》 运行 -》 运行
自旋等待锁被释放第一种方式线程会被挂起和恢复,而第二种线程一直处于运行状态,因此自旋锁高效很多!
2.重量级锁加锁方式:
monitorenter -> 执行同步代码块 -》 monitorexit
3.轻量级锁加锁方式:CAS(设置对象头轻量级锁标志)-》执行代码块-》CAS(重置对象头轻量级锁标志)
CAS相对于monitorenter和monitorexit的代价更小轻量级锁不是为了替换重量级锁的而是为了减少使用重量级锁,当轻量级锁失败后,会升级为重量级锁。
4.MCSLock自旋锁实现
MCS 来自于其发明人名字的首字母: John Mellor-Crummey和Michael Scott。是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,直接前驱负责通知其结束自旋,从而极大地减少了不必要的处理器缓存同步的次数,降低了总线和内存的开销。
package com.mzj.javabase.thread.spinlock.demo2._04_MCSLock;
import com.mzj.javabase.thread.spinlock.demo2.Lock;
import java.util.concurrent.atomic.AtomicReference;
public class MCSLock implements Lock {
private AtomicReference<QNode> tail;
private ThreadLocal<QNode> myNode;
public MCSLock() {
tail = new AtomicReference<>(null);
myNode = ThreadLocal.withInitial(QNode::new);
}
@Override
public void lock() {
QNode qnode = myNode.get();
QNode preNode = tail.getAndSet(qnode);
if (preNode != null) {
qnode.locked = false;
preNode.next = qnode;
//wait until predecessor gives up the lock
while (!qnode.locked) {
}
}
qnode.locked = true;
}
@Override
public void unlock() {
QNode qnode = myNode.get();
if (qnode.next == null) {
//后面没有等待线程的情况
if (tail.compareAndSet(qnode, null)) {
//真的没有等待线程,则直接返回,不需要通知
return;
}
//wait until predecessor fills in its next field
// 突然有人排在自己后面了,可能还不知道是谁,下面是等待后续者
while (qnode.next == null) {
}
}
//后面有等待线程,则通知后面的线程
qnode.next.locked = true;
qnode.next = null;
}
private class QNode {
/**
* 是否被qNode所属线程锁定
*/
private volatile boolean locked = false;
/**
* 与CLHLock相比,多了这个真正的next
*/
private volatile QNode next = null;
}
}
版权声明:本文为Master_Shifu_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。