一 UML類圖
1.1、ReentrantLock
通過類圖ReentrantLock是同步鎖,同一時間只能有一個線程獲取到鎖,其他獲取該鎖的線程會被阻塞而被放入AQS阻塞隊列中。ReentrantLock類繼承Lock接口;內部抽象類Sync實現抽象隊列同步器AbstractQueuedSynchronizer;Sync類有兩個子類NonfairSync(非公平鎖)和FairSync(公平鎖),默認構造方法使用非公平鎖,可以使用帶布爾參數的構造方法指定使用公平鎖;ReentrantLock可以創建多個條件進行綁定。
1.2、AbstractQueuedSynchronizer
AbstractQueuedSynchronizer:抽象隊列同步器,維護一個volatile int state變量標識共享資源和一個FIFO線程阻塞隊列(AQS隊列)。
protected final void setState(int newState):設置state值
protected final int getState():獲取state值
protected final boolean compareAndSetState(int expect, int update):CAS設置state值
AQS有兩種共享資源類型:SHARED(共享)和EXCLUSIVE(獨佔),針對類型有不同的方法:
protected boolean tryAcquire(int arg):獨佔類型獲取鎖
protected boolean tryRelease(int arg):獨佔類型釋放鎖
protected int tryAcquireShared(int arg):共享類型獲取鎖
protected boolean tryReleaseShared(int arg):共享類型釋放鎖
protected boolean isHeldExclusively():是否是獨佔類型
1.3、線程節點類型waitStatus
AQS隊列中節點的waitStatus枚舉值(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node)含義:
static final int CANCELLED = 1; //線程被取消
static final int SIGNAL = -1; //成功的線程需要被喚醒
static final int CONDITION = -2; //線程在條件隊列中等待
static final int PROPAGATE = -3; //釋放鎖是需要通知其他節點
二 原理分析
2.1 獲取鎖
2.1.1 void lock()方法
調用線程T調用該方法嘗試獲取當前鎖。
①如果鎖未被其他線程獲取,則調用線程T成功獲取到當前鎖,然後設置當前鎖的擁有者為調用線程T,並設置AQS的狀態值state為1,然後直接返回。
②如果調用線程T之前已經獲取當前鎖,則只設置設置AQS的狀態值state加1,然後返回。
③如果當前鎖已被其他線程獲取,則調用線程T放入AQS隊列后阻塞掛起。
public void lock() {
sync.lock();//委託內部公平鎖和非公平鎖獲取鎖
}
//非公平鎖
final void lock() {
if (compareAndSetState(0, 1))//設置AQS狀態值為1
setExclusiveOwnerThread(Thread.currentThread());//成功設置鎖的線程擁有者
else acquire(1);//失敗加入到AQS隊列阻塞掛起
}
//公平鎖
final void lock() {
acquire(1);
}
非公平鎖分析:
//調用父類AbstractOwnableSynchronizer方法CAS設置state值,成功返回true,失敗返回false
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//調用父類AbstractOwnableSynchronizer方法,設置當前線程為鎖的擁有者
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
//調用AbstractQueuedSynchronizer父類方法,第一次獲取鎖失敗
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//排它鎖類型
selfInterrupt();
}
//NonfairSync子類,重寫父類方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
//Sync類非公平鎖嘗試獲取鎖
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//二次獲取鎖 if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//當前線程已獲取鎖,AQS狀態值加1 int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//AbstractQueuedSynchronizer類創建節點,添加到AQS隊列後面
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);//創建AQS隊列的節點,節點類型排它鎖
Node pred = tail;//尾結點 if (pred != null) {
node.prev = pred;//新節點的前一個節點是尾結點 if (compareAndSetTail(pred, node)) {//CAS機制添加節點
pred.next = node;//尾結點執行新的節點 return node;
}
}
enq(node);
return node;
}
//插入節點到隊列中
private Node enq(final Node node) {
for (;;) {//循環的方式,直至創建成功
Node t = tail;//尾結點 if (t == null) { //尾結點為空,初始化
if (compareAndSetHead(new Node()))//第一步:CAS創建頭結點(哨兵節點)一個空節點
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//第二步:CAS設置尾結點
t.next = node;
return t;
}
}
}
}
//
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();//前向節點 if (p == head && tryAcquire(arg)) {//如果p節點是頭結點,node作為隊列第二個節點
setHead(node);//將頭節點設置為node節點,node節點出隊列
p.next = null; //原頭結點設置為空,便於GC回收
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);//失敗解鎖
}
}
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
//阻塞掛起當前線程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
//
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);//大於0代表線程被取消
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
//線程阻塞,打斷線程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
公平鎖分析:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {//與非公平鎖相比,區別就在標紅的位置
setExclusiveOwnerThread(current);
return true;
}
}else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
//①h != t:表示AQS隊列頭結點和尾結點不相同,隊列不為空;
//②(s = h.next) == null || s.thread != Thread.currentThread():頭結點(哨兵節點)為空或者next節點不等於當前線程 return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
2.1.2 void lockInterruptibly()方法
與2.2.1方法相似,不同之處在於:該方法對中斷進行響應,其他線程調用當前線程中斷方法,拋出InterruptedException。
2.1.3 boolean tryLock()方法
嘗試獲取鎖。注意:該方法不會引起當前線程阻塞。
2.1.4 boolean tryLock(long timeout, TimeUnit unit)方法
與2.1.3方法相似,不同之處在於:設置了超時時間。
2.2 釋放鎖
嘗試釋放鎖。
如果當前線程T已獲取鎖,則調用該方法更新AQS狀態值減1。如果當前狀態值為0,則釋放鎖;如果當前狀態值部位0,則只是減1操作。
如果當前線程T未獲取鎖,則調用該方法是會拋出IllegalMonitorStateException異常。
2.2.1 void unlock()方法
public void unlock() {
sync.release(1);
}
//調用AbstractQueuedSynchronizer方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//喚醒線程 return true;
}
return false;
}
//回調Sync類釋放鎖
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);//設置鎖的擁有線程為空
}
setState(c);
return free;
}
//
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;//線程阻塞等待狀態 if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);//CAS設置狀態 /*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)//遍歷AQS隊列 if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//喚醒線程
}
h != t
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!
※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※教你寫出一流的銷售文案?
※別再煩惱如何寫文案,掌握八大原則!