Java鎖的種類以及辨析(二):自旋鎖的其他種類
阿新 • • 發佈:2018-12-22
作者:山雞
鎖作為併發共享資料,保證一致性的工具,在JAVA平臺有多種實現(如 synchronized 和 ReentrantLock等等 ) 。這些已經寫好提供的鎖為我們開發提供了便利,但是鎖的具體性質以及型別卻很少被提及。本系列文章將分析JAVA下常見的鎖名稱以及特性,為大家答疑解惑。
2.自旋鎖的其他種類
上篇我們講到了自旋鎖,在自旋鎖中 另有三種常見的鎖形式:TicketLock ,CLHlock 和MCSlock
Ticket鎖主要解決的是訪問順序的問題,主要的問題是在多核cpu上
package com.alipay.titan.dcc.dal.entity; 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); } }
每次都要查詢一個serviceNum 服務號,影響效能(必須要到主記憶體讀取,並阻止其他cpu修改)。
CLHLock 和MCSLock 則是兩種型別相似的公平鎖,採用連結串列的形式進行排序,
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public class CLHLock { public static class CLHNode { private volatile boolean isLocked = true; } @SuppressWarnings("unused") 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; } }
CLHlock是不停的查詢前驅變數, 導致不適合在NUMA 架構下使用(在這種結構下,每個執行緒分佈在不同的實體記憶體區域)
MCSLock則是對本地變數的節點進行迴圈。不存在CLHlock 的問題。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public class MCSLock { public static class MCSNode { volatile MCSNode next; volatile boolean isLocked = true; } private static final ThreadLocal<MCSNode> NODE = new ThreadLocal<MCSNode>(); @SuppressWarnings("unused") 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.isLocked) { } } } 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.isLocked = false; currentNode.next = null; } } }
從程式碼上 看,CLH 要比 MCS 更簡單,
CLH 的佇列是隱式的佇列,沒有真實的後繼結點屬性。
MCS 的佇列是顯式的佇列,有真實的後繼結點屬性。
JUC ReentrantLock 預設內部使用的鎖 即是 CLH鎖(有很多改進的地方,將自旋鎖換成了阻塞鎖等等)。