- Ticket锁主要解决的是访问顺序的问题,主要的问题是在多核CPU上,代码如下,每次都要查询一个serviceNum 服务号,影响性能(必须要到主内存读取,并阻止其他CPU修改)。
import java.util.concurrent.atomic.AtomicInteger;
public class TicketLock {
private AtomicInteger serviceNum = new AtomicInteger();
private AtomicInteger ticketNum = new AtomicInteger();
private static final ThreadLocal<Integer> local = new ThreadLocal<Integer>();
public void lock() {
int myticket = ticketNum.getAndIncrement();
local.set(myticket);
while (myticket != serviceNum.get()) {}
}
public void unlock() {
int myticket = local.get();
serviceNum.compareAndSet(myticket, myticket + 1);
}
}
- CLHLock:Craig, Landin, and Hagersten Locks,是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性;CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋;
- 当一个线程需要获取锁时:
- 创建一个的CLHNode,将其中的locked设置为true表示需要获取锁;
- 线程对tail域调用getAndSet方法,使自己加入到队列的尾部,同时获取一个指向其前趋结点的引用preNode;
- 该线程就在前趋结点的locked字段上旋转,直到前趋结点释放锁;
- 当一个线程需要释放锁时,将当前结点的locked域设置为false,同时回收前趋结点;
- 示例代码如下:
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class CLHLock {
public static class CLHNode {
private volatile boolean isLocked = true;
}
private volatile CLHNode tail;
private static final ThreadLocal<CLHNode> local = new ThreadLocal<CLHNode>();
private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> updater =
AtomicReferenceFieldUpdater.newUpdater(CLHLock.class, CLHNode.class, "tail");
public void lock() {
CLHNode node = new CLHNode();
local.set(node);
CLHNode preNode = updater.getAndSet(this, node);
if (preNode != null) {
while (preNode.isLocked) {}
preNode = null;
local.set(node);
}
}
public void unlock() {
CLHNode node = local.get();
if (!updater.compareAndSet(this, node, null)) {
node.isLocked = false;
}
node = null;
}
}
- MCSLock则是对本地变量的节点进行循环。MSC与CLH最大的不同并不是链表是显示还是隐式,而是线程自旋的规则不同:CLH是在前趋结点的locked域上自旋等待,而MCS是在自己的结点的locked域上自旋等待。正因为如此,它解决了CLH在NUMA系统架构中获取locked域状态内存过远的问题;不存在CLHlock 的问题。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class MCSLock {
public static class MCSNode {
volatile MCSNode next;
volatile boolean locked = true;
}
private static final ThreadLocal<MCSNode> node = new ThreadLocal<MCSNode>();
private volatile MCSNode queue;
private static final AtomicReferenceFieldUpdater<MCSLock, MCSNode> updater =
AtomicReferenceFieldUpdater.newUpdater(MCSLock.class, MCSNode.class, "queue");
public void lock() {
MCSNode currentNode = new MCSNode();
node.set(currentNode);
MCSNode preNode = updater.getAndSet(this, currentNode);
if (preNode != null) {
preNode.next = currentNode;
while (currentNode.locked) {}
}
}
public void unlock() {
MCSNode currentNode = node.get();
if (currentNode.next == null) {
if (updater.compareAndSet(this, currentNode, null)) {
} else {
while (currentNode.next == null) {}
}
} else {
currentNode.next.locked = false;
currentNode.next = null;
}
}
}