多线程Monitor工作原理
🍎1. 什么是Monitor?
我們都知道synchronized的作用是用來保證修飾的代碼或者方法執行有且只有一個線程執行,也就是鎖。那么在執行被鎖住的方式時,synchronized就需要通過monitor來記錄和保證鎖的狀態。所以monitor這里的作用其實就是起到了控制synchronized什么時候獲取鎖,什么時候釋放鎖,以及記錄了鎖被重用的次數。🍎2. 當多線程時Monitor如何執行
前置知識了解
owner : 指向的是當前獲得線程的地址,用來判斷當前鎖是被哪個線程持有。
waitSet : 是指已經獲取得一次鎖了,對象調用了wait方法,講當前線程掛起了就進入了等待隊列。等待時間到期的時候喚醒,或者其他線程喚醒。
entryList : 是隊列用來獲取鎖的緩沖區,用來將cxq和waitSet中的數據 移動到entryList進行排隊。這個統一獲取鎖的入口。一般是cxq 或者waitSet數據復制過來進行統一排隊。
執行流程
剛開始Monitor中Owner為null當Thread-2執行synchronized(obj)就會將Monitor的所有者Owner置為Thread-2,Monitor中只能有一個Owner
在Thread-2上鎖的過程中,如果Thread-3,Thread-.4,Thread-5也來執行synchronized(obj),就會進入EntryList BLOCKED(阻塞)
Thread-2執行完同步代碼塊的內容,然后喚醒EntryList中等待的線程來競爭鎖,競爭的時是非公平的圖中WaitSet中的Thread-0,Thread-1是之前獲得過鎖,但條件不滿足進入WAITING狀態的線程,
🍎3. 以為字節碼舉例
對于被synchronized修飾的代碼塊,在生成class字節碼文件中會出現monitorenter、monitorexit。如下面例子所示:
public void synBlock();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=3, args_size=10: aload_01: dup2: astore_13: monitorenter #14: getstatic 7: ldc 9: invokevirtual 12: aload_113: monitorexit #214: goto 17: astore_218: aload_119: monitorexit #320: aload_221: athrow22: return執行monitorenter的線程會嘗試獲取monitor的所有權,會發生以下三種情況之一:
如果該monitor的計數為0,這線程獲得該monitor鎖并設置為1如果當前線程有了這個monitor鎖,則該線程的monitor的計數累加1
如果其他線程嘗試獲取monitor鎖,發現monitor的計數不為0,這表示當前線程被其他線程占用,則阻塞,直到這個monitor鎖的計數變為0,然后再重新嘗試獲取。
執行monitorexit的線程就會將montior的計數減1,直到減到0為止,這時候就表示可以釋放當前montior的鎖了,其他的線程就可以嘗試來獲取當前代碼的鎖了。
看到這里,可能會有疑問,為什么生成的字節碼文件中,一個monitorenter為什么對存在兩個monitorexit,這里其實是考慮到代碼發生了異常的情況,當我們在正常執行完任務之后,會執行#2的monitorexit去釋放鎖,但是出現異常了就會去執行#3的monitorexit的鎖。這樣就避免了死鎖的發生,保證在任何情況下都能正常釋放鎖。
被修飾的同步方法
同步代碼塊是使用monitorenter和monitorexit來實現的,對于方法則不是依靠它兩來實現的而是通過一個ACC_SYNCHRONIZED的flag修飾符,源代碼如下:
public synchronized void synMethod();descriptor: ()Vflags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack=0, locals=1, args_size=10: returnLineNumberTable:line 16: 0當某個線程需要訪問這個方法的時候,會先檢查這個方法是否有ACC_SYNCHRONIZED這個標簽,如果有就需要先獲取monitor鎖,其他的方面和同步代碼塊的邏輯是一樣的。
總結
以上是生活随笔為你收集整理的多线程Monitor工作原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux more 下一页_Linux
- 下一篇: 安卓系统培训!从入门到精通的Androi