Android 線程安全(二) synchronized

synchronized 關鍵字

一、使用方式

1、修飾普通方法,鎖對象爲類實例
2、修飾靜態方法,鎖對象爲類class實例
3、修飾代碼塊,鎖對象爲括號裏的對象

    private synchronized void fun(){//  普通方法互斥

    }

    public synchronized static void funStatic(){//  靜態方法互斥

    }

        synchronized (object){
			//代碼塊互斥
        }

這樣使用後各線程都是互斥的,保證了線程安全。

二、鎖優化

參考自《深入理解java虛擬機》java 1.6 以後 jvm 優化了 synchronized。一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,這幾個狀態會隨着競爭情況逐漸升級。

優化
1、鎖粗化(Lock Coarsening):也就是減少不必要的緊連在一起的unlock,lock操作,將多個連續的鎖擴展成一個範圍更大的鎖,減少了鎖獲取釋放的次數。

private void fun1(){
        synchronized (object){
            //do somethings 1
        }
        synchronized (object){
            //do somethings 2
        }
        synchronized (object){
            //do somethings 3
        }
    }
    //粗化合到一起
    private void fun2(){
        synchronized (object){
            //do somethings 1
            //do somethings 2
            //do somethings 3
        }
    }

2、鎖消除(Lock Elimination):鎖削除是指虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享數據競爭的鎖進行削除。
3、適應性自旋(Adaptive Spinning):自旋是指獲取鎖時,先不掛起線程,而是while循環使用CAS的方式獲取,這樣效率更高。適應性自旋則會根據之前當前線程獲取鎖的結果,來動態調整while循環的次數, 優化了效率,更“智能”。

java中每個對象都可以作爲鎖對象,因爲每個對象都擁有一個對象頭,記錄對象hashCode、GC分代、偏向鎖編程ID,鎖狀態等。
正如圖展示的這部分內存,不是固定的,在不通的鎖狀態有不通的指向。

對象頭
1、無鎖狀態,如果jvm檢測到不可能有多線程執行此方法,則是無鎖狀態,內存就是表格第一行描述的那樣。表格中第1行。
2、加入一個線程調用了方法或者代碼塊,鎖對象的線程ID使用CAS標記當前線程,在沒有其他線程調用的情況下,不會再CAS,直接就調用了。表格中第5行。
3、如果在偏向鎖的狀態下,有其他線程調用,線程ID與當前調用線程不一致,則 膨脹爲輕量級鎖。膨脹過程爲:使用CAS在當前線程棧幀複製此對象頭信息保存在Lock Recorder空間,並將鎖對象頭指向此棧幀中的內存。就是說當前線程獲取了此輕量級鎖。表格中第2行。
如果一個線程調用方法,使用CAS複製失敗,發現markwork指向當前棧幀內存,就直接調用了。

4、如果一個線程調用方法,使用CAS複製失敗,而且markword不指向當前棧幀,則膨脹爲重量級鎖,將markwork指向鎖對象。本線程掛起,並等待持有鎖釋放。表格第三行。

三、重量級鎖

重量級鎖會將線程掛起,等待執行。

重量級鎖

上圖簡單描述多線程獲取鎖的過程,當多個線程同時訪問一段同步代碼時,首先會進入 Entry Set當線程獲取到對象的monitor 後進入 The Owner 區域並把monitor中的owner變量設置爲當前線程,同時monitor中的計數器count加1,若線程調用 wait() 方法,將釋放當前持有的monitor,owner變量恢復爲null,count自減1,同時該線程進入 WaitSet集合中等待被喚醒。若當前線程執行完畢也將釋放monitor(鎖)並復位變量的值,以便其他線程進入獲取monitor(鎖)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章