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