java重入锁,再探JAVA重入锁
之前的文章中簡(jiǎn)單的為大家介紹了重入鎖JAVA并發(fā)之多線程基礎(chǔ)(2)。這里面也是簡(jiǎn)單的為大家介紹了重入鎖的幾種性質(zhì),這里我們就去探索下里面是如何實(shí)現(xiàn)的。
我們知道在使用的時(shí)候,必須鎖先有定義,然后我們?cè)倌弥?dāng)前的鎖進(jìn)行加鎖操作,然后處理業(yè)務(wù),最后是釋放鎖的操作(這里就拿里面非公平鎖的實(shí)現(xiàn)來(lái)講解)。
字節(jié)碼操作
public class com.montos.lock.ReentrantLockDemo implements java.lang.Runnable {
public static java.util.concurrent.locks.ReentrantLock lock;
public static int k;
public com.montos.lock.ReentrantLockDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public void run();
Code:
0: iconst_0
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 29 //int類型的值進(jìn)行棧頂比較
9: getstatic #2 // Field lock:Ljava/util/concurrent/locks/ReentrantLock;
12: invokevirtual #3 // Method java/util/concurrent/locks/ReentrantLock.lock:()V
15: getstatic #4 // Field k:I
18: iconst_1
19: iadd
20: putstatic #4 // Field k:I
23: iinc 1, 1
26: goto 2
29: iconst_0
30: istore_1
31: iload_1
32: sipush 1000
35: if_icmpge 50
38: getstatic #2 // Field lock:Ljava/util/concurrent/locks/ReentrantLock;
41: invokevirtual #5 // Method java/util/concurrent/locks/ReentrantLock.unlock:()V
44: iinc 1, 1
47: goto 31
50: return
public static void main(java.lang.String[]) throws java.lang.InterruptedException;
Code:
0: new #6 // class com/montos/lock/ReentrantLockDemo
3: dup
4: invokespecial #7 // Method "":()V
7: astore_1
8: new #8 // class java/lang/Thread
11: dup
12: aload_1
13: invokespecial #9 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V
16: astore_2
17: new #8 // class java/lang/Thread
20: dup
21: aload_1
22: invokespecial #9 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V
25: astore_3
26: aload_2
27: invokevirtual #10 // Method java/lang/Thread.start:()V
30: aload_3
31: invokevirtual #10 // Method java/lang/Thread.start:()V
34: aload_2
35: invokevirtual #11 // Method java/lang/Thread.join:()V
38: aload_3
39: invokevirtual #11 // Method java/lang/Thread.join:()V
42: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
45: getstatic #4 // Field k:I
48: invokevirtual #13 // Method java/io/PrintStream.println:(I)V
51: return
static {};
Code:
0: new #14 // class java/util/concurrent/locks/ReentrantLock
3: dup
4: invokespecial #15 // Method java/util/concurrent/locks/ReentrantLock."":()V
7: putstatic #2 // Field lock:Ljava/util/concurrent/locks/ReentrantLock;
10: iconst_0
11: putstatic #4 // Field k:I
14: return
}
復(fù)制代碼
這里面無(wú)非就是入棧,棧元素比較,出棧放入變量中這些操作,沒有之前的synchronized里面的監(jiān)視器相關(guān)指令限制,只是簡(jiǎn)單的一些棧操作。
加鎖操作
final void lock(){
if (compareAndSetState(0, 1)) //將同步狀態(tài)從0變成1 采用cas進(jìn)行更新
setExclusiveOwnerThread(Thread.currentThread());//設(shè)置當(dāng)前擁有獨(dú)占訪問權(quán)的線程。
else
acquire(1);//沒有獲取到鎖,則進(jìn)行嘗試操作
}
復(fù)制代碼
往下面的選擇走:
public final void acquire(int arg){
//先進(jìn)行再次嘗試獲取鎖的操作,如果獲取失敗則將當(dāng)前加入隊(duì)列中,并設(shè)置中斷標(biāo)志。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
復(fù)制代碼
首先走嘗試獲取鎖的操作(這里還是走非公平鎖的):
final boolean nonfairTryAcquire(int acquires){
final Thread current = Thread.currentThread();//拿到當(dāng)前線程
int c = getState();//同步狀態(tài)
if (c == 0) {//再次做獲取鎖的操作
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//是否是當(dāng)前線程已經(jīng)占有
int nextc = c + acquires;//原本的狀態(tài)數(shù)值+當(dāng)前傳入數(shù)值
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);//設(shè)置新的狀態(tài)
return true;
}
return false;
}
復(fù)制代碼
接著往下走:
private Node addWaiter(Node mode){
//獨(dú)占模式進(jìn)行封裝當(dāng)前線程
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {如果尾節(jié)點(diǎn)不為null,將當(dāng)前的節(jié)點(diǎn)接入并返回
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
復(fù)制代碼
繼續(xù)往下走:
private Node enq(final Node node){
for (;;) {//
Node t = tail;
if (t == null) { // 初始化尾節(jié)點(diǎn)
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {//尾節(jié)點(diǎn)與當(dāng)前的節(jié)點(diǎn)互換
t.next = node;
return t;//返回當(dāng)前節(jié)點(diǎn)
}
}
}
}
復(fù)制代碼
接著回去往下走:
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)) {//如果當(dāng)前節(jié)點(diǎn)前一個(gè)節(jié)點(diǎn)是頭節(jié)點(diǎn),并嘗試獲鎖成功
setHead(node);//設(shè)置當(dāng)前的頭結(jié)點(diǎn)
p.next = null; // 手動(dòng)清除引用 幫助GC
failed = false;
return interrupted;
}
//檢測(cè)獲取鎖失敗的節(jié)點(diǎn)狀態(tài) 以及暫時(shí)掛起并返回當(dāng)前的中斷標(biāo)志
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);//取消正在進(jìn)行的獲取嘗試。
}
}
復(fù)制代碼
說真的,咱們直接看失敗的情況,我們接著往下走:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node){
//檢查和更新無(wú)法獲取的節(jié)點(diǎn)的狀態(tài)。
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//該節(jié)點(diǎn)已經(jīng)設(shè)置了請(qǐng)求釋放信號(hào)狀態(tài),所以可以進(jìn)行安全掛起
return true;
if (ws > 0) {
do {//清除不需要執(zhí)行的節(jié)點(diǎn)
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//waitstatus必須為0或傳播。表明我們需要信號(hào),但不要掛起。調(diào)用者重試以確保在掛起前無(wú)法獲取。
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
復(fù)制代碼
然后看向下一個(gè)方法:
private final boolean parkAndCheckInterrupt(){
LockSupport.park(this);//掛起當(dāng)前線程
return Thread.interrupted();//返回中斷標(biāo)識(shí)
}
復(fù)制代碼
上面的取消獲取隊(duì)列里面的節(jié)點(diǎn)就不看了..cancelAcquire(node),里面就是取消正在進(jìn)行的獲取嘗試。同時(shí)將無(wú)需的節(jié)點(diǎn)移除。當(dāng)上面的操作走完之后就設(shè)置當(dāng)前線程中斷標(biāo)識(shí)。這里面主要流程是說如果加鎖不成功之后,對(duì)于當(dāng)前線程是怎么執(zhí)行操作的,我們可以看到,里面的方法中大部分在獲取不到鎖之后,下一步操作中會(huì)再次嘗試獲取下,如果獲取不到才會(huì)繼續(xù)執(zhí)行,獲取到了我們就可以直接使用,這里也是多線程操作里面的魅力,每一個(gè)空隙中就可能會(huì)讓當(dāng)前線程進(jìn)行獲得鎖的操作。
釋放鎖操作
釋放鎖的步驟就簡(jiǎn)單許多了:
public final boolean release(int arg){
if (tryRelease(arg)) {//嘗試釋放鎖
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//喚醒節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)
return true;
}
return false;
}
復(fù)制代碼
咱們繼續(xù)往下看:
protected final boolean tryRelease(int releases){
int c = getState() - releases;//同步狀態(tài)-當(dāng)前釋放狀態(tài)值
if (Thread.currentThread() != getExclusiveOwnerThread())//如果當(dāng)前線程不是拿鎖線程,則報(bào)監(jiān)視器相關(guān)錯(cuò)誤
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;//只有當(dāng)前重入次數(shù)為0,才能返回true
setExclusiveOwnerThread(null);//當(dāng)前獨(dú)占線程設(shè)為NULL
}
setState(c);//重新設(shè)置同步狀態(tài)
return free;
}
復(fù)制代碼
然后往下走:
private void unparkSuccessor(Node node){
//當(dāng)前狀態(tài)為負(fù)數(shù),則嘗試清除當(dāng)前的線程狀態(tài)
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//清除取消或無(wú)效的節(jié)點(diǎn),從尾部向后移動(dòng)以找到實(shí)際節(jié)點(diǎn)
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);//釋放當(dāng)前線程
}
復(fù)制代碼從上面的順序往下面來(lái)看,我們主要發(fā)現(xiàn)線程在拿鎖階段是有許多的操作的,要根據(jù)線程的狀態(tài)再將線程從等待隊(duì)列中移除。釋放的時(shí)候就顯得簡(jiǎn)潔了許多,我們只需要看到當(dāng)前線程的狀態(tài)-1,然后看看是否是重入的。
我們通過一個(gè)簡(jiǎn)單的重入鎖代碼可以看到,作者在用無(wú)鎖的操作去獲得鎖,這個(gè)整體的步驟里面考慮的東西很多,每一個(gè)時(shí)刻,線程都有可能千變?nèi)f化,我們需要了解的是我們每一個(gè)步驟都需要可能發(fā)生的情況。如果能夠考慮到發(fā)生的情況,那么有些步驟就可以直接跳過,我們直接就可以獲得最后的結(jié)果(這塊在線程嘗試獲鎖的階段可以體現(xiàn))。有小伙伴對(duì)于重入鎖還有什么看法的可以在下面進(jìn)行留言,我們可以相互學(xué)習(xí),共同進(jìn)步~
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java重入锁,再探JAVA重入锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win10设置文件夹背景色
- 下一篇: iaas层次化结构--从业务需求到设计需