Jvm知識學習(九)-鎖

一,線程安全
1,示例說明:多線程網站統計訪問人數 
使用鎖,維護計數器的串行訪問與安全性
多線程訪問ArrayList
代碼:

public static List<Integer> numberList =new ArrayList<Integer>();

public static class AddToList implements Runnable{

  int startnum=0;

  public AddToList(int startnumber){

  startnum=startnumber;

  }

  @Override

  public void run() {

  int count=0;

  while(count<1000000){

  numberList.add(startnum);

  startnum+=2;

  count++;

  }

  }

}

運行結果:

解釋說明:出現錯誤,你因爲列表在增加長度時,同時被另一個線程訪問,最終輸出的數據也表明代碼存在線程安全問題。

二,對象頭Mark
解釋:Mark Word,對象頭的標記,32位 ,描述對象的hash、鎖信息,垃圾回收標記,年齡 
記錄的信息:指向鎖記錄的指針 ,指向monitor的指針 ,GC標記 ,偏向鎖線程ID等。 

三,不是Java語言層面的幾種鎖
1,偏向鎖
大部分情況是沒有競爭的,所以可以通過偏向來提高性能
所謂的偏向,就是偏心,即鎖會偏向於當前已經佔有鎖的線程
將對象頭Mark的標記設置爲偏向,並將線程ID寫入對象頭Mark
只要沒有競爭,獲得偏向鎖的線程,在將來進入同步塊,不需要做同步
當其他線程請求相同的鎖時,偏向模式結束
-XX:+UseBiasedLocking
默認啓用
在競爭激烈的場合,偏向鎖會增加系統負擔
示例:

2,輕量級鎖
BasicObjectLock :嵌入在線程棧中的對象 
普通的鎖處理性能不夠理想,輕量級鎖是一種快速的鎖定方法。
如果對象沒有被鎖定
將對象頭的Mark指針保存到鎖對象中
將對象頭設置爲指向鎖的指針(在線程棧空間中)

如果輕量級鎖失敗,表示存在競爭,升級爲重量級鎖(常規鎖)
在沒有鎖競爭的前提下,減少傳統鎖使用OS互斥量產生的性能損耗
在競爭激烈時,輕量級鎖會多做很多額外操作,導致性能下降
3,自旋鎖
當競爭存在時,如果線程可以很快獲得鎖,那麼可以不在OS層掛起線程,讓線程做幾個空操作(自旋)
JDK1.6中-XX:+UseSpinning開啓
JDK1.7中,去掉此參數,改爲內置實現
如果同步塊很長,自旋失敗,會降低系統性能
如果同步塊很短,自旋成功,節省線程掛起切換時間,提升系統性能
總結:
不是Java語言層面的鎖優化方法
內置於JVM中的獲取鎖的優化方法和獲取鎖的步驟
偏向鎖可用會先嚐試偏向鎖
輕量級鎖可用會先嚐試輕量級鎖
以上都失敗,嘗試自旋鎖
再失敗,嘗試普通鎖,使用OS互斥量在操作系統層掛起

四,鎖的性能方法
1,減少鎖持有時間
鎖在同一時間只能允許一個線程持有,其它想要佔用鎖的線程都得在臨界區外等待鎖的釋放,這個等待的時間根據實際的應用及代碼寫法可長可短

減少了其它線程等待鎖的時長,因此,也就提高了程序的性能,使鎖的使用得到優化。

2,減少鎖粒度
將大對象,拆成小對象,大大增加並行度,降低鎖競爭
偏向鎖,輕量級鎖成功率提高
ConcurrentHashMap
HashMap的同步實現
Collections.synchronizedMap(Map<K,V> m)
返回SynchronizedMap對象


3,鎖分離


4,鎖粗化
通常情況下,爲了保證多線程間的有效併發,會要求每個線程持有鎖的時間儘量短,即在使用完公共資源後,應該立即釋放鎖。只有這樣,等待在這個鎖上的其他線程才能儘早的獲得資源執行任務。但是,凡事都有一個度,如果對同一個鎖不停的進行請求、同步和釋放,其本身也會消耗系統寶貴的資源,反而不利於性能的優化
例如:


5,鎖消除
在即時編譯器時,如果發現不可能被共享的對象,則可以消除這些對象的鎖操作
示例:

分析:從源碼中可以看出,append方法用了synchronized關鍵詞,它是線程安全的。但我們可能僅在線程內部把StringBuffer當作局部變量使用:
代碼中createStringBuffer方法中的局部對象sBuf,就只在該方法內的作用域有效,不同線程同時調用createStringBuffer()方法時,都會創建不同的sBuf對象,因此此時的append操作若是使用同步操作,就是白白浪費的系統資源。
這時我們可以通過編譯器將其優化,將鎖消除,前提是java必須運行在server模式(server模式會比client模式作更多的優化),同時必須開啓逃逸分析:
-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
其中+DoEscapeAnalysis表示開啓逃逸分析,+EliminateLocks表示鎖消除。
逃逸分析:比如上面的代碼,它要看sBuf是否可能逃出它的作用域?如果將sBuf作爲方法的返回值進行返回,那麼它在方法外部可能被當作一個全局對象使用,就有可能發生線程安全問題,這時就可以說sBuf這個對象發生逃逸了,
因而不應將append操作的鎖消除,但我們上面的代碼沒有發生鎖逃逸,鎖消除就可以帶來一定的性能提升。

6,無鎖
鎖是悲觀的操作
無鎖是樂觀的操作
無鎖的一種實現方式
CAS(Compare And Swap)
非阻塞的同步
CAS(V,E,N)
在應用層面判斷多線程的干擾,如果有干擾,則通知線程重試
示例:


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