摘要:一般情況下,可以從兩個(gè)角度進(jìn)行鎖優(yōu)化對(duì)單個(gè)鎖算法的優(yōu)化和對(duì)鎖粒度的細(xì)分。單個(gè)鎖的優(yōu)化自旋鎖非自旋鎖在未獲取鎖的情況會(huì)被阻塞,之后再喚醒嘗試獲得鎖。
Java鎖優(yōu)化
應(yīng)用程序在并發(fā)環(huán)境下會(huì)產(chǎn)生很多問題,通常情況下,我們可以通過加鎖來解決多線程對(duì)臨界資源的訪問問題。但是加鎖往往會(huì)成為系統(tǒng)的瓶頸,因?yàn)榧渔i和釋放鎖會(huì)涉及到與操作系統(tǒng)的交互,會(huì)有很大的性能問題。那么這個(gè)時(shí)候基于鎖的優(yōu)化手段就顯得很重要了。
一般情況下,可以從兩個(gè)角度進(jìn)行鎖優(yōu)化:對(duì)單個(gè)鎖算法的優(yōu)化和對(duì)鎖粒度的細(xì)分。
1. 單個(gè)鎖的優(yōu)化 自旋鎖:? 非自旋鎖在未獲取鎖的情況會(huì)被阻塞,之后再喚醒嘗試獲得鎖。而JDK的阻塞和喚醒是基于操作系統(tǒng)實(shí)現(xiàn)的,會(huì)有系統(tǒng)資源的開銷。自旋鎖就是線程不停地循環(huán)嘗試獲得鎖,而不會(huì)將自己阻塞,這樣不會(huì)浪費(fèi)系統(tǒng)的資源開銷,但是會(huì)浪費(fèi)CPU的資源。所有現(xiàn)在的JDK大部分都是先自旋等待,如果自旋等待一段時(shí)間之后還沒有獲取到鎖,就會(huì)將當(dāng)前線程阻塞。
鎖消除:? 當(dāng)JVM分析代碼時(shí)發(fā)現(xiàn)某個(gè)方法只被單個(gè)線程安全訪問,而且這個(gè)方法是同步方法,那么JVM就會(huì)去掉這個(gè)方法的鎖。
單個(gè)鎖優(yōu)化的瓶頸:? 對(duì)單個(gè)鎖優(yōu)化的效果就像提高單個(gè)CPU的處理能力一樣,最終會(huì)由于各個(gè)方面的限制而達(dá)到一個(gè)平衡點(diǎn),到達(dá)這個(gè)點(diǎn)之后優(yōu)化單個(gè)鎖的對(duì)高并發(fā)下面鎖的優(yōu)化效果越來越低。所以將一個(gè)鎖進(jìn)行粒度細(xì)分帶來的效果會(huì)很明顯,如果一個(gè)鎖保護(hù)的代碼塊被拆分成兩個(gè)鎖來保護(hù),那么程序的效率就大約能夠提高到2倍,這個(gè)比單個(gè)鎖的優(yōu)化帶來的效果要明顯很多。常見的鎖粒度細(xì)分技術(shù)有:鎖分解和鎖分段
2. 細(xì)分鎖粒度細(xì)分鎖粒度的目的是降低競爭鎖的概率。
2.1 鎖分解鎖分解的核心是將無關(guān)的代碼塊,如果在一個(gè)方法中有一部分的代碼與鎖無關(guān),一部分的代碼與鎖有關(guān),那么可以縮小這個(gè)鎖的返回,這樣鎖操作的代碼塊就會(huì)減少,鎖競爭的可能性也會(huì)減少
縮小鎖的范圍縮小鎖的范圍是指盡量只在必要的地方加鎖,不要擴(kuò)大加鎖的范圍,就拿單例模式舉例,范圍大的鎖可能將整個(gè)方法都加鎖了:
class Singleton { private Singleton instance; private Singleton() { } // 將整個(gè)方法加鎖 public synchronized Singleton getInstance() { try { Thread.sleep(1000); //do something if(null == instance) instance = new Singleton(); } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }
優(yōu)化后的,只將部分代碼加鎖:
class Singleton { private Singleton instance; private Singleton() { } public Singleton getInstance() { try { Thread.sleep(1000); //do something // 只對(duì)部分代碼加鎖 synchronized(this) { if(null == instance) instance = new Singleton(); } } catch (InterruptedException e) { e.printStackTrace(); } return instance; } }減少鎖的粒度
減少鎖的粒度是指如果一個(gè)鎖需要保護(hù)多個(gè)相互獨(dú)立的變量,那么可以將一個(gè)鎖分解為多個(gè)鎖,并且每個(gè)鎖保護(hù)一個(gè)變量,這樣就可以減少鎖沖突。看一下下面的例子:
class Demo{ private SetallUsers = new HashSet (); private Set allComputers = new HashSet (); //公用一把鎖 public synchronized void addUser(String user){ allUsers.add(user); } public synchronized void addComputer(String computer){ allComputers.add(computer); } }
縮小鎖的粒度后,將一個(gè)鎖拆分為多個(gè):
class Demo{ private SetallUsers = new HashSet (); private Set allComputers = new HashSet (); //分解為兩把鎖 public void addUser(String user){ synchronized (allUsers){ allUsers.add(user); } } public void addComputer(String computer){ synchronized (allComputers){ allComputers.add(computer); } } }
如上的方法把一個(gè)鎖分解為2個(gè)鎖時(shí)候,采用兩個(gè)線程時(shí)候,大約能夠使程序的效率提升一倍。
2.2 鎖分段鎖分段和縮小鎖的粒度類似,就是將鎖細(xì)分的粒度更多,比如將一個(gè)數(shù)組的每個(gè)位置當(dāng)做多帶帶的鎖。JDK8以前ConcurrentHashMap就使用了鎖分段技術(shù),它將散列數(shù)組分成多個(gè)Segment,每個(gè)Segment存儲(chǔ)了實(shí)際的數(shù)據(jù),訪問數(shù)據(jù)的時(shí)候只需要對(duì)數(shù)據(jù)所在的Segment加鎖就行。
參考:Java鎖分解鎖分段技術(shù): http://guochenglai.com/2016/0...
ConcurrentHashMap的鎖分段技術(shù):https://blog.csdn.net/yansong...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69626.html
摘要:自選鎖鎖膨脹后,虛擬機(jī)為了避免線程真實(shí)地在操作系統(tǒng)層面掛起,虛擬機(jī)還會(huì)在做最后的努力自選鎖。 showImg(https://segmentfault.com/img/remote/1460000016159660?w=500&h=333); 作為一款公用平臺(tái),JDK 本身也為并發(fā)程序的性能絞盡腦汁,在 JDK 內(nèi)部也想盡一切辦法提供并發(fā)時(shí)的系統(tǒng)吞吐量。這里,我將向大家簡單介紹幾種 J...
摘要:無論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)保持者,也就說,在任何時(shí)刻最多只能有一個(gè)執(zhí)行單元獲得鎖。另外在中引入了自適應(yīng)的自旋鎖。和關(guān)鍵字的總結(jié)推薦一 該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識(shí))。地址:https://github.com/Snailclimb... 本文是對(duì) synchronized 關(guān)鍵字使用、底層原理、JD...
摘要:使用可以禁止的指令重排,保證在多線程環(huán)境下也能正常運(yùn)行。關(guān)鍵字底層原理總結(jié)關(guān)鍵字底層原理屬于層面。另外在中引入了自適應(yīng)的自旋鎖。自適應(yīng)的自旋鎖帶來的改進(jìn)就是自旋的時(shí)間不在固定了,而是和前一次同一個(gè)鎖上的自旋時(shí)間以及鎖的擁有者 【強(qiáng)烈推薦!非廣告!】阿里云雙11褥羊毛活動(dòng):https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCc...
摘要:前置知識(shí)點(diǎn)對(duì)象頭要了解鎖優(yōu)化策略中的輕量級(jí)鎖與偏向鎖的原理和運(yùn)作過程,需要先了解虛擬機(jī)的對(duì)象頭部分的內(nèi)存布局。否則說明這個(gè)鎖對(duì)象已經(jīng)被其他線程搶占了。 前置知識(shí)點(diǎn):對(duì)象頭 要了解鎖優(yōu)化策略中的輕量級(jí)鎖與偏向鎖的原理和運(yùn)作過程,需要先了解Hotspot虛擬機(jī)的對(duì)象頭部分的內(nèi)存布局。 對(duì)象頭(摘自《深入理解java虛擬機(jī)》) 對(duì)象頭信息是與對(duì)象自身定義的數(shù)據(jù)無關(guān)的額外存儲(chǔ)成本 如果對(duì)象是數(shù)...
摘要:并發(fā)需要解決的問題功能性問題線程同步面臨兩個(gè)問題,想象下有兩個(gè)線程在協(xié)作工作完成某項(xiàng)任務(wù)。鎖可用于規(guī)定一個(gè)臨界區(qū),同一時(shí)間臨界區(qū)內(nèi)僅能由一個(gè)線程訪問。并發(fā)的數(shù)據(jù)結(jié)構(gòu)線程安全的容器,如等。 并發(fā)指在宏觀上的同一時(shí)間內(nèi)同時(shí)執(zhí)行多個(gè)任務(wù)。為了滿足這一需求,現(xiàn)代的操作系統(tǒng)都抽象出 線程 的概念,供上層應(yīng)用使用。 這篇博文不打算詳細(xì)展開分析,而是對(duì)java并發(fā)中的概念和工具做一個(gè)梳理。沿著并發(fā)模...
閱讀 1211·2021-11-24 10:43
閱讀 3235·2021-11-22 09:34
閱讀 3610·2021-10-08 10:04
閱讀 4011·2021-09-23 11:58
閱讀 3166·2019-08-30 15:44
閱讀 543·2019-08-30 13:01
閱讀 1231·2019-08-28 18:07
閱讀 1501·2019-08-26 13:42