动态

详情 返回 返回

synchronized - 动态 详情

synchronized為什麼是可重入的
簡單理解就是當前線程已經持有了對象鎖,當前線程可以繼續訪問

synchronized底層使用的是lock+cmpxchg
synchronized是非公平鎖,當一個線程要獲取鎖時,先試圖插隊,如果佔用線程釋放了鎖,其他線程沒有獲取鎖,那麼當前線程就可以獲取鎖,如果鎖被其他線程佔用,那麼加入到waitset中,排隊,排隊的時候不能獲取鎖,只能等前面所有的等待線程獲取鎖之後才能獲取

鎖升級過程
無鎖:沒有任何線程持有對象鎖
偏向鎖:當只有一個線程訪問的時候,在markword中記錄偏向線程的id
輕量級鎖:當出現鎖競爭的時候,會採用自旋形式獲取鎖,一般會自旋10次,當自旋結束還沒獲取到鎖,升級為重量級鎖。
重量級鎖:需要像os申請mutex互斥量

鎖只能升級,不能降級,hotspot是這麼實現的。
輕量級鎖,只是在用户態自旋,不需要切換到內核態,但是輕量級鎖浪費cpu
重量級鎖,需要切換到內核態申請mutex資源,浪費時間
所以輕量級鎖比重量級鎖快

鎖消除和鎖粗化
鎖消除:虛擬機對不存在共享數據的鎖進行消除,
鎖消除需要虛擬機開啓逃逸分析
鎖粗化:多個加鎖、解鎖操作連在一起,擴大一個範圍更大的鎖

        //鎖消除
        public void test() {
            Object object = new Object();
            synchronized(object) {
                //鎖只對單次方法調用起作用,其實沒有意義
            }
        }
        
        //鎖粗化
        Object lock = new Object();
        public void add() {
            for(int i = 0; i< 100; i++) {
                 synchronized(lock) {
                 }
            }
        }
        //粗化後
        public void add() {
            synchronized(lock) {
                for(int i = 0; i< 100; i++) {
                
                }
            }
        }

image.png
001表示無鎖,101表示偏向鎖,00表示輕量級鎖,01 表示重量級鎖,11 表示GC

自旋鎖在jdk1.4.2中引入,使用-XX:+UseSpinning來開啓,在jdk1.6中默認開啓,並引入了自適應自旋鎖
自旋鎖升級為輕量級鎖:有線程自旋超過10次,-XX:PreBlockSpin,或者自旋超過cpu核數一半,1.6之後,加入了自適應自旋,由jvm來控制
升級為重量級鎖:jvm是運行在用户態的進程,需要像操作系統申請mutex資源,cpu從ring3用户態切換到ring0內核態,線程掛起進入waitset,等待操作系統調度,返回用户空間。

synchronized是重入鎖,重入的次數必須記錄,因為要解鎖幾次必須要對應
鎖重入次數,偏向鎖和輕量級鎖記錄到LockRecord中,重量級鎖記錄到ObjectMonitor中

為什麼有自旋鎖,還需要重量級鎖?
自旋消耗cpu資源,如果鎖時間過長,或者有自旋線程較多,cpu資源會被大量浪費,直接升級為重量級鎖,進入等下隊列,節約cpu資源

偏向鎖是否一定比自旋鎖效率高?
不一定,在明確知道多線程競爭的情況下,偏向鎖會設計鎖撤銷操作,這時候直接使用自旋鎖效率較高,
偏向鎖有個延時,默認是4s,-XX:BiasedLockingStartupDelay=0,因為jvm啓動的時候,會默認啓動一些線程,裏面會涉及很多同步操作,啓動的時候肯定會有競爭,如果使用偏向鎖,就會設計鎖撤銷和鎖升級過程,效率較低。

image.png

Add a new 评论

Some HTML is okay.