實戰Java高併發程式設計(四、鎖的優化及注意事項)
阿新 • • 發佈:2018-11-21
在多核時代,使用多執行緒可以明顯地提升系統的效能。但事實上,使用多執行緒會額外增加系統的開銷。對於單任務或單執行緒的應用來說,其主要資源消耗在任務本身。對於多執行緒來說,系統除了處理功能需求外,還需要維護多執行緒環境特有的資訊,如執行緒本身的元資料,執行緒的排程,執行緒上下文的切換等。
4.1有助於提高鎖效能的建議
4.1.1減少鎖持有的時間
如果執行緒持有鎖的時間越長,鎖的競爭程度越激烈。
4.1.2減少鎖粒度
鎖粒度:假如一個執行緒任務是在家裡上廁所,家就是一個執行緒,衛生間鎖門就是加鎖。如果馬桶,浴缸,洗漱臺都是隔開相對獨立的,實際上衛生間可以同時給三個人使用,那麼再細化鎖粒度,就可以對馬桶,浴缸,洗漱臺分別加鎖。
但是當想要獲取全域性資訊時,需要獲取所有段的鎖才能順利實施。所以減少鎖粒度要慎重。
4.1.3讀寫分離鎖來替換獨佔鎖
在讀多寫少的場合,使用讀寫鎖可以有效提升系統的併發能力。
4.1.4鎖分離
依據應用程式的功能特點,使用類似的分離思想,對獨佔鎖進行分離。
4.2ThreadLocal
ThreadLocal的作用是提供執行緒內的區域性變數,這種變數線上程的生命週期內起作用,減少同一個執行緒內多個函式或者元件之間一些公共變數的傳遞的複雜度。舉個例子,我出門需要先坐公交再做地鐵,這裡的坐公交和坐地鐵就好比是同一個執行緒內的兩個函式,我就是一個執行緒,我要完成這兩個函式都需要同一個東西:公交卡(北京公交和地鐵都使用公交卡),那麼我為了不向這兩個函式都傳遞公交卡這個變數(相當於不是一直帶著公交卡上路),我可以這麼做:將公交卡事先交給一個機構,當我需要刷卡的時候再向這個機構要公交卡(當然每次拿的都是同一張公交卡)。這樣就能達到只要是我(同一個執行緒)需要公交卡,何時何地都能向這個機構要的目的。
舉例:有5個執行緒,這5個執行緒都有一個值value,初始值為0,執行緒執行時用一個迴圈往value值相加數字。
public class Test { private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>() { // 初始化 @Override protected Integer initialValue() { return 0; } }; public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(new MyThread(i)).start(); } } static class MyThread implements Runnable { private int index; public MyThread(int index) { this.index = index; } @Override public void run() { System.out.println("執行緒" + index + "的初始value:" + value.get()); for (int i = 0; i < 10; i++) { value.set(value.get() + i); } System.out.println("執行緒" + index + "的累加value:" + value.get()); } } }
5個執行緒之間互不影響。
4.3無鎖
後續補充