Java高并發(fā)下鎖的優(yōu)化詳解
簡述
鎖是最常用的同步方法之一。在高并發(fā)的環(huán)境下,激烈的鎖競爭會導致程序的性能下降。
下面是一些關(guān)于鎖的使用建議,可以把這種副作用降到最低。
減少鎖持有時間
對于使用鎖進行并發(fā)控制的應(yīng)用程序而言,在鎖競爭過程中,單個線程對鎖的持有時間與系統(tǒng)性能有著直接的關(guān)系。
如果線程持有鎖的時間很長,那么相對地,鎖的競爭程序也就越激烈。
應(yīng)該盡可能地減少對某個鎖的占有時間,以減少程序間互斥的可能。
public synchronized void syncMethod() {
otherCode1(); //無同步控制需要
needSynMethod(); //有同步控制需要
otherCode2(); //無同步控制需要
}
public void syncMethod2() {
otherCode1(); //無同步控制需要
synchronized(this) {
needSynMethod(); //有同步控制需要
}
otherCode2(); //無同步控制需要
}
說明:減少鎖的持有時間有助于降低鎖沖突的可能性,進而提升系統(tǒng)的并發(fā)能力。
減少鎖粒度
減少鎖粒度也是一種削弱多線程鎖競爭的有效手段。這種技術(shù)典型的使用場景就是ConcurrentHashMap類的實現(xiàn)。ConcurrentHashMap和Hashtable主要區(qū)別就是圍繞著鎖的粒度以及如何鎖,可以簡單理解成把一個大的HashTable分解成多個,形成了鎖分離。而Hashtable的實現(xiàn)方式是—鎖整個hash表。concurrentHashMap內(nèi)部細分了若干個小的hashMap,稱之為段(segment),默認情況下,被細分為16個段。新增的時候根據(jù)key的hashcode計算出應(yīng)該存放到哪一個段中,然后對這個段枷鎖,完成put()操作。就是說,最多可以同時接收16個線程同時插入(前提是16個不同段插入),從而大大提高吞吐量。但是,減少鎖粒度會引入一個新的問題,即:當系統(tǒng)需要取得全局鎖時,其消耗的資源會比較多。需要遍歷每一個段,對每一個段進行加鎖,最后還要對每一個段進行解鎖。concurrentHashMap的size()方法會先使用無鎖的方式求和,如果失敗才會嘗試這種加鎖的方法。所以,在高并發(fā)場合,size()的性能差于同步的Hashmap,適用于size()調(diào)用少的場合。 說明:所謂減少鎖粒度,就是指縮小鎖定對象的范圍,從而減少鎖沖突的可能性,進行提供系統(tǒng)的并發(fā)能力。
讀寫分離鎖來替換獨占鎖
ReadWriteLock讀寫分離鎖替代獨占鎖是減少鎖粒度的一種特殊情況。減少鎖粒度是通過分割數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)的,而讀寫鎖則是對系統(tǒng)功能點的分割。 讀操作本身不會影響數(shù)據(jù)的完整性和一致性。因此,理論上講,在大部分情況下,應(yīng)該可以允許多想成同時讀。 讀寫鎖的訪問約束情況:讀-讀(非阻塞)、讀-寫(阻塞)、寫-讀(阻塞)、寫-寫(阻塞)
public class ReadWriteLockTest2 {
public static void main(String[] args) {
//創(chuàng)建一個鎖對象
ReadWriteLock lock = new ReentrantReadWriteLock(false);
//創(chuàng)建一個線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//創(chuàng)建一些并發(fā)訪問
RWRun rw1 = new RWRun(lock,true);
RWRun rw2 = new RWRun(lock,true);
RWRun rw3 = new RWRun(lock,false);
//在線程池中執(zhí)行各個的操作
pool.execute(rw1);
pool.execute(rw2);
pool.execute(rw3);
//關(guān)閉線程池
pool.shutdown();
}
}
class RWRun implements Runnable {
private ReadWriteLock myLock; //執(zhí)行操作所需的鎖對象
private boolean ischeck; //是否查詢
public RWRun(ReadWriteLock myLock, boolean ischeck) {
super();
this.myLock = myLock;
this.ischeck = ischeck;
}
public void run() {
if (ischeck) {
//獲取讀鎖
myLock.readLock().lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//釋放讀鎖
myLock.readLock().unlock();
} else {
//獲取寫鎖
myLock.writeLock().lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//釋放寫鎖
myLock.writeLock().unlock();
}
}
}上面執(zhí)行完的時間為2秒鐘,如果是獨占鎖 就需要3秒鐘 結(jié)論:在讀多寫少的場合,使用讀寫鎖可以有效提升系統(tǒng)的并發(fā)能力。
鎖分離
在LinkedBlockingQueue的實現(xiàn)中,take()和put()分別實現(xiàn)了從隊列中取得數(shù)據(jù)和往隊列中增加數(shù)據(jù)的功能。
雖然兩個函數(shù)都對當前隊列進行了修改操作,但由于是基于鏈表的,因此,兩個操作分別作用于隊列的前端和尾端,從理論上來說,兩者并不沖突。
所以在JDK中,采用了兩把不同的鎖,分離了toke()和put()的操作,實現(xiàn)了可并發(fā)的操作。
//take()函數(shù)需要持有的鎖 private final ReentrantLock takeLock = new ReentrantLock(); private final Condition notEmpty = takeLock.newCondition(); //put()函數(shù)需要持有的鎖 private final ReentrantLock putLock = new ReentrantLock(); private final Condition notFull = putLock.newCondition();
鎖粗化
虛擬機在遇到一連串連續(xù)地對同一鎖不斷進行請求和釋放的操作時,便會把所有的鎖操作整合成對鎖的一次請求,從而減少對鎖的請求同步次數(shù),這個操作叫做鎖的粗化。
例子:在循環(huán)內(nèi)請求鎖時。
for (int i =0 i < n; i++) {
synchronized (lock) {
doSomething();
}
}更加合理的做法應(yīng)該是在外層只請求一次鎖
到此這篇關(guān)于Java高并發(fā)下鎖的優(yōu)化詳解的文章就介紹到這了,更多相關(guān)高并發(fā)下鎖的優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中HashMap和TreeMap的區(qū)別深入理解
首先介紹一下什么是Map。在數(shù)組中我們是通過數(shù)組下標來對其內(nèi)容索引的,而在Map中我們通過對象來對對象進行索引,用來索引的對象叫做key,其對應(yīng)的對象叫做value2012-12-12
MyBatis-Plus中使用EntityWrappe進行列表數(shù)據(jù)倒序設(shè)置方式
這篇文章主要介紹了MyBatis-Plus中使用EntityWrappe進行列表數(shù)據(jù)倒序設(shè)置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Java實現(xiàn)將數(shù)據(jù)導出為Word文檔的方法步驟
我們在開發(fā)一些系統(tǒng)的時候,例如OA系統(tǒng),經(jīng)常能遇到將審批單數(shù)據(jù)導出為word和excel文檔的需求,導出為excel是比較簡單的,但是word文檔的格式不像表格那樣可以輕松的定位,所以本文給大家介紹了Java怎樣實現(xiàn)將數(shù)據(jù)導出為Word文檔,需要的朋友可以參考下2025-01-01
SpringMVC中controller接收json數(shù)據(jù)的方法
這篇文章主要為大家詳細介紹了SpringMVC中controller接收json數(shù)據(jù)的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09
詳解Java Bellman-Ford算法原理及實現(xiàn)
Bellman-Ford算法與Dijkstra算法類似,都是以松弛操作作為基礎(chǔ),Bellman-Ford算法是對所有邊都進行松弛操作,本文將詳解Bellman-Ford算法原理及實現(xiàn),感興趣的可以了解一下2022-07-07

