ReentrantLock
是java.util.concurrent.locks
包下一個可重入的默認是非公平的鎖,ReentrantLock
類是Lock
接口的一個使用很頻繁的實現類,類結構以下圖:java
前面說過JMM
模型要求的是可見性,原子性和有序性。解決原子性的方法也有多種,例如synchronized
同步方法或者同步代碼塊,也可使用AtomicInteger
原子包裝類解決,都知道synchronized
加鎖是最笨重的解決方法,所裏,這裏使用的是ReentrantLock
加鎖來實現原子性,代碼以下:node
class MyData {
int num = 0;
Lock lock = new ReentrantLock();
public void add() {
try {
lock.lock();
num++;
} finally {
lock.unlock();
}
}
}
public class ReentrantLockDemo {
private static Logger log = LoggerFactory.getLogger(ReentrantLockDemo.class);
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
myData.add();
}
}, String.valueOf(i)).start();
}
// 知道全部的線程執行結束
while (Thread.activeCount() > 1) {
Thread.yield();
}
log.info("結果:{}", myData.num);
}
}
複製代碼
ReentrantLock
提供了一個帶參數的構造函數,來讓使用者決定使用是不是公平鎖。算法
經過源碼咱們能夠知道,無參數就是默認爲非公平鎖,傳入true
表示公平鎖,傳入false
表示非公平鎖,源碼以下安全
// 空參默認爲非公平鎖
public ReentrantLock() {
sync = new NonfairSync();
}
複製代碼
// 根絕參數決定鎖的公平性
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
複製代碼
// 建立一個非公平鎖
Lock lock = new ReentrantLock();
try {
// 加鎖
lock.lock();
} finally {
// 釋放鎖
lock.unlock();
}
複製代碼
public void lock() {
sync.lock();
}
複製代碼
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */
final void lock() {
// 經過CAS思想去AQS隊列中獲取將State值從0變爲1,即獲取到鎖
if (compareAndSetState(0, 1))
// 獲取鎖成功則將當前線程標記爲持有鎖的線程,而後直接返回
setExclusiveOwnerThread(Thread.currentThread());
else
// 獲取鎖失敗則執行該方法
acquire(1);
}
}
複製代碼
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
// unsafe類是經過native方法直接操做內存數據
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
複製代碼
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
複製代碼
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
複製代碼
tryAcquire
這個方法是AQS
默認的鉤子方法,不一樣類的有不一樣的實現,其中NonfairSync
實現以下:函數
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
複製代碼
final boolean nonfairTryAcquire(int acquires) {
// 傳入的acquires爲1,獲取當前線程
final Thread current = Thread.currentThread();
// 獲取state變量的值,即當前鎖被重入的次數
int c = getState();
// state爲0,說明當前鎖未被任何線程持有
if (c == 0) {
// 經過CAS思想去AQS隊列中獲取將State值從0變爲1,即獲取到鎖
if (compareAndSetState(0, acquires)) {
// 獲取鎖成功則將當前線程標記爲持有鎖的線程,而後直接返回
setExclusiveOwnerThread(current);
// 返回嘗試獲取鎖成功
return true;
}
}
// //當前線程就是持有鎖的線程,說明該鎖被重入了
else if (current == getExclusiveOwnerThread()) {
// //計算state變量要更新的值
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// //非同步方式更新state值
setState(nextc);
// 獲取鎖成功,返回結果
return true;
}
// 嘗試獲取鎖失敗
return false;
}
複製代碼
private Node addWaiter(Node mode) {
// 創造一個新的節點,傳入的mode參數爲null
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// tail是指向隊列尾元素的尾指針,新節點的頭指針指向隊列的尾指針
Node pred = tail;
// //隊列不爲空
if (pred != null) {
// 新節點的頭指針修改成隊列的尾指針
node.prev = pred;
// 使用CAS算法,若是內存中的隊列仍是以前的尾指針就把新節點指向尾指針
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 安全的加入同步隊列
enq(node);
return node;
}
複製代碼
private Node enq(final Node node) {
for (;;) {
// t節點指向當前隊列的最後一個節點
Node t = tail;
// 隊列爲空
if (t == null) { // Must initialize
// 經過CAS構造新節點
if (compareAndSetHead(new Node()))
// 尾指針指向新節點
tail = head;
} else {
// 隊列不爲空時候,將節點的頭指針指向隊列的尾指針
node.prev = t;
// 使用CAS算法,若是內存中的隊列仍是以前的尾指針就把新節點指向尾指針
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
複製代碼
當隊列爲空的時候經過CAS更新頭節點源碼以下:ui
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
複製代碼
說明:僅當隊列中原值爲null時更新成功。this
當隊列不爲空的時候經過CAS更新尾節點源碼以下:spa
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
複製代碼
說明:CAS方式更新tial指針,僅當原值爲t時更新成功線程
final boolean acquireQueued(final Node node, int arg) {
// 參數arg爲1,節點是爲獲取到鎖的線程節點
boolean failed = true;
try {
boolean interrupted = false;
// 進入死循環,正常狀況下線程只有得到鎖才能跳出循環
for (;;) {
// 獲取當前節點線程的前驅節點
final Node p = node.predecessor();
// 當獲取到了前驅節點爲隊列頭節點或者嘗試獲取鎖成功
if (p == head && tryAcquire(arg)) {
// 設置當前線程的節點爲頭節點
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted; // 死循環的惟一出口
}
if (shouldParkAfterFailedAcquire(p, node) && // 判斷是否要阻塞當前線程
parkAndCheckInterrupt()) // 阻塞當前線程
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
複製代碼
lock.unlock();
複製代碼
public void unlock() {
sync.release(1);
}
複製代碼
public final boolean release(int arg) {
// 釋放鎖(state-1),若釋放後鎖可被其餘線程獲取(state=0),返回true
if (tryRelease(arg)) {
// 獲取隊列的頭節點
Node h = head;
//當前隊列不爲空且頭結點狀態不爲初始化狀態(0)
if (h != null && h.waitStatus != 0)
// 喚醒同步隊列中被阻塞的線程
unparkSuccessor(h);
return true;
}
return false;
}
複製代碼
protected final boolean tryRelease(int releases) {
// 計算待更新的state值
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 待更新的state值爲0,說明持有鎖的線程未重入,一旦釋放鎖其餘線程將能獲取
if (c == 0) {
free = true;
// 清除鎖的持有線程標記
setExclusiveOwnerThread(null);
}
// 更新的state值
setState(c);
return free;
}
複製代碼
咱們能夠看見一個很重要的抽象類AbstractQueuedSynchronizer
,有關AQS
稍後在死磕,能夠說AQS
是同步組件的基礎。指針