鎖優化(5種方法)

轉載:http://www.importnew.com/21353.html

1. 鎖優化的思路和方法

鎖優化的思路和方法有以下幾種:

  • 減少鎖持有時間
  • 減小鎖粒度
  • 鎖分離
  • 鎖粗化
  • 鎖消除

1.1 減少鎖持有時間

public synchronized void syncMethod(){  
        othercode1();  
        mutextMethod();  
        othercode2(); 
    }

像上述代碼這樣,在進入方法前就要得到鎖,其他線程就要在外面等待。

這里優化的一點在于,要減少其他線程等待的時間,所以,只需要在有線程安全要求的程序代碼上加鎖。

public void syncMethod(){  
        othercode1();  
        synchronized(this)
        {
            mutextMethod();  
        }
        othercode2(); 
    }

1.2 減小鎖粒度

將大對象(這個對象可能會被很多線程訪問),拆成小對象,大大增加并行度,降低鎖競爭。降低了鎖的競爭,偏向鎖,輕量級鎖成功率才會提高。

最最典型的減小鎖粒度的案例就是ConcurrentHashMap。

1.3 鎖分離

最常見的鎖分離就是讀寫鎖ReadWriteLock,根據功能進行分離成讀鎖和寫鎖,這樣讀讀不互斥,讀寫互斥,寫寫互斥。即保證了線程安全,又提高了性能。

讀寫分離思想可以延伸,只要操作互不影響,鎖就可以分離。

比如LinkedBlockingQueue


這里寫圖片描述

從頭部取出數據,從尾部放入數據,使用兩把鎖。

1.4 鎖粗化

通常情況下,為了保證多線程間的有效并發,會要求每個線程持有鎖的時間盡量短,即在使用完公共資源后,應該立即釋放鎖。只有這樣,等待在這個鎖上的其他線程才能盡早的獲得資源執行任務。

但是,凡事都有一個度,如果對同一個鎖不停的進行請求、同步和釋放,其本身也會消耗系統寶貴的資源,反而不利于性能的優化 。

舉個例子:

public void demoMethod(){  
        synchronized(lock){   
            //do sth.  
        }  
        //...做其他不需要的同步的工作,但能很快執行完畢  
        synchronized(lock){   
            //do sth.  
        } 
    }

這種情況,根據鎖粗化的思想,應該合并:

public void demoMethod(){  
        //整合成一次鎖請求 
        synchronized(lock){   
            //do sth.   
            //...做其他不需要的同步的工作,但能很快執行完畢  
        }
    }

當然這是有前提的,前提就是中間的那些不需要同步的工作是很快執行完成的。

再舉一個極端的例子:

for(int i = 0; i < CIRCLE; i++){  
            synchronized(lock){  
                 //...
            } 
        }

在一個循環內不同得獲得鎖。雖然JDK內部會對這個代碼做些優化,但是還不如直接寫成:

synchronized(lock){ 
            for(int i=0;i<CIRCLE;i++){ 
 
            } 
        }

當然如果有需求說,這樣的循環太久,需要給其他線程不要等待太久,那只能寫成上面那種。如果沒有這樣類似的需求,還是直接寫成下面那種比較好。

1.5 鎖消除

鎖消除是在編譯器級別的事情。

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

也許你會覺得奇怪,既然有些對象不可能被多線程訪問,那為什么要加鎖呢?寫代碼時直接不加鎖不就好了。

但是有時,這些鎖并不是程序員所寫的,有的是JDK實現中就有鎖的,比如Vector和StringBuffer這樣的類,它們中的很多方法都是有鎖的。當我們在一些不會有線程安全的情況下使用這些類的方法時,達到某些條件時,編譯器會將鎖消除來提高性能。

比如:

public static void main(String args[]) throws InterruptedException {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 2000000; i++) {
            createStringBuffer("JVM", "Diagnosis");
        }
        long bufferCost = System.currentTimeMillis() - start;
        System.out.println("craeteStringBuffer: " + bufferCost + " ms");
    }
 
    public static String createStringBuffer(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb.toString();
    }

上述代碼中的StringBuffer.append是一個同步操作,但是StringBuffer卻是一個局部變量,并且方法也并沒有把StringBuffer返回,所以不可能會有多線程去訪問它。

那么此時StringBuffer中的同步操作就是沒有意義的。

開啟鎖消除是在JVM參數上設置的,當然需要在server模式下:

-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

并且要開啟逃逸分析。 逃逸分析的作用呢,就是看看變量是否有可能逃出作用域的范圍。

比如上述的StringBuffer,上述代碼中craeteStringBuffer的返回是一個String,所以這個局部變量StringBuffer在其他地方都不會被使用。如果將craeteStringBuffer改成

public static StringBuffer craeteStringBuffer(String s1, String s2) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        return sb;
    }

那么這個 StringBuffer被返回后,是有可能被任何其他地方所使用的(譬如被主函數將返回結果put進map啊等等)。那么JVM的逃逸分析可以分析出,這個局部變量 StringBuffer逃出了它的作用域。

所以基于逃逸分析,JVM可以判斷,如果這個局部變量StringBuffer并沒有逃出它的作用域,那么可以確定這個StringBuffer并不會被多線程所訪問,那么就可以把這些多余的鎖給去掉來提高性能。

當JVM參數為:

-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

輸出:

craeteStringBuffer: 302 ms

JVM參數為:

-server -XX:+DoEscapeAnalysis -XX:-EliminateLocks

輸出:

craeteStringBuffer: 660 ms

顯然,鎖消除的效果還是很明顯的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容

  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區別 13、...
    Miley_MOJIE閱讀 3,725評論 0 11
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,737評論 18 399
  • 相關概念 面向對象的三個特征 封裝,繼承,多態.這個應該是人人皆知.有時候也會加上抽象. 多態的好處 允許不同類對...
    東經315度閱讀 1,976評論 0 8
  • 聽說你過的不好 聽說你和前女友沒有過了七年之癢 聽說你一下子就知道好友添加是她 聽說你恢復單身 聽說她坐十多個小時...
    我是小表姐閱讀 162評論 0 0
  • 前一段文章講到,我在火車站偶遇到她并且厚著臉皮自導自演了一出搭訕,那既然是辛辛苦苦贏到的機會,我怎么可能讓她在指縫...
    齊小生閱讀 554評論 0 1