并发优化–减少锁粒度
在這種情況下,我們有一個(gè)競爭條件 ,其中只有一個(gè)線程(在資源上)將獲得鎖,而所有其他需要該鎖的線程將被阻塞。 此同步功能不是免費(fèi)提供的。 JVM和OS都會(huì)消耗資源,以便為您提供有效的并發(fā)模型。 使并發(fā)實(shí)現(xiàn)資源密集的三個(gè)最基本的因素是:
- 上下文切換
- 內(nèi)存同步
- 封鎖
為了編寫優(yōu)化的代碼進(jìn)行同步,您必須了解這三個(gè)因素以及如何減少它們。 編寫此類代碼時(shí),必須注意許多事項(xiàng)。 在本文中,我將向您展示一種通過減少鎖的粒度來減少這些因素的技術(shù)。
從基本規(guī)則開始: 不要將鎖持有超過必要的時(shí)間。
獲取鎖之前 ,請做任何您需要做的事情, 僅將鎖用于對同步資源進(jìn)行操作并立即釋放它。 看一個(gè)簡單的例子:
public class HelloSync {private Map dictionary = new HashMap();public synchronized void borringDeveloper(String key, String value) {long startTime = (new java.util.Date()).getTime();value = value + "_"+startTime;dictionary.put(key, value);System.out.println("I did this in "+((new java.util.Date()).getTime() - startTime)+" miliseconds");} }在此示例中,我們違反了基本規(guī)則,因?yàn)槲覀儎?chuàng)建了兩個(gè)Date對象,調(diào)用System.out.println()并執(zhí)行許多String串聯(lián)。 唯一需要同步的動(dòng)作是動(dòng)作:“ dictionary.put(key,value); ”更改代碼并將同步從方法范圍移到這一行。 更好的代碼是這樣的:
public class HelloSync {private Map dictionary = new HashMap();public void borringDeveloper(String key, String value) {long startTime = (new java.util.Date()).getTime();value = value + "_"+startTime;synchronized (dictionary) {dictionary.put(key, value);}System.out.println("I did this in "+((new java.util.Date()).getTime() - startTime)+" miliseconds");} }上面的代碼可以寫得更好,但是我只想給你個(gè)想法。 如果想知道如何做,請檢查java.util.concurrent.ConcurrentHashMap 。
那么,如何減少鎖的粒度呢? 簡而言之,通過盡可能少地請求鎖。 基本思想是使用單獨(dú)的鎖來保護(hù)一個(gè)類的多個(gè)獨(dú)立狀態(tài)變量,而不是在類范圍內(nèi)僅具有一個(gè)鎖。 看看我在許多應(yīng)用程序中看到的這個(gè)簡單示例。
public class Grocery {private final ArrayList fruits = new ArrayList();private final ArrayList vegetables = new ArrayList();public synchronized void addFruit(int index, String fruit) {fruits.add(index, fruit);}public synchronized void removeFruit(int index) {fruits.remove(index);}public synchronized void addVegetable(int index, String vegetable) {vegetables.add(index, vegetable);}public synchronized void removeVegetable(int index) {vegetables.remove(index);} }雜貨店老板可以在他的雜貨店添加水果和蔬菜/從中刪除水果和蔬菜。 雜貨店的這種實(shí)現(xiàn)使用基本的雜貨店鎖來保護(hù)水果和蔬菜,因?yàn)橥绞窃诜椒ǚ秶鷥?nèi)完成的。 除了使用這種胖鎖,我們可以使用兩個(gè)單獨(dú)的守護(hù)程序,每種資源(水果和蔬菜)使用一個(gè)。 檢查下面的改進(jìn)代碼。
public class Grocery {private final ArrayList fruits = new ArrayList();private final ArrayList vegetables = new ArrayList();public void addFruit(int index, String fruit) {synchronized(fruits) fruits.add(index, fruit);}public void removeFruit(int index) {synchronized(fruits) {fruits.remove(index);}}public void addVegetable(int index, String vegetable) {synchronized(vegetables) vegetables.add(index, vegetable);}public void removeVegetable(int index) {synchronized(vegetables) vegetables.remove(index);} }使用兩個(gè)防護(hù)裝置(拆分鎖)后,我們看到的鎖定流量將少于原始的胖鎖。 當(dāng)我們將其應(yīng)用于具有中等鎖爭用的鎖時(shí),此技術(shù)會(huì)更好地工作。 如果我們將其應(yīng)用于具有輕微爭用的鎖,則收益很小,但仍為正值。 如果我們將其應(yīng)用于爭用較大的鎖,則結(jié)果并不總是更好,您必須意識(shí)到這一點(diǎn)。
請出于良心使用此技術(shù)。 如果您懷疑這是一個(gè)爭用鎖,請按照以下步驟操作:
- 確認(rèn)您的生產(chǎn)需求量,將其乘以3或5(即使您愿意準(zhǔn)備,甚至乘以10)。
- 根據(jù)新流量在您的測試平臺(tái)上運(yùn)行適當(dāng)?shù)臏y試。
- 比較兩個(gè)解決方案,然后才選擇最合適的解決方案。
有更多技術(shù)可以提高同步性能,但是對于所有技術(shù),基本規(guī)則是: 保持鎖的時(shí)間不要超過必要的時(shí)間 。
正如我已經(jīng)向您解釋的那樣,此基本規(guī)則可以翻譯為“盡可能少地尋求鎖”,也可以翻譯為其他翻譯(解決方案),我將在以后的文章中嘗試描述它們。
另外兩個(gè)重要的建議:
- 請注意java.util.concurrent包(和子包) 中的類,因?yàn)樗鼈冇蟹浅B斆骱陀杏玫膶?shí)現(xiàn)。
- 通過使用良好的設(shè)計(jì)模式,大多數(shù)時(shí)候并發(fā)代碼可以最小化。 始終牢記企業(yè)集成模式 ,它們可以節(jié)省您的時(shí)間。
參考: 減少鎖粒度–我們Java的 JCG合作伙伴 Adrianos Dadis的并發(fā)優(yōu)化 ,Integration and the sources 的優(yōu)點(diǎn) 。
相關(guān)文章 :- Java并發(fā)教程–信號(hào)量
- Java并發(fā)教程–重入鎖
- Java并發(fā)教程–線程池
- Java并發(fā)教程–可調(diào)用,將來
- Java并發(fā)教程–阻塞隊(duì)列
- Java并發(fā)教程– CountDownLatch
- Java Fork / Join進(jìn)行并行編程
- Java內(nèi)存模型–快速概述和注意事項(xiàng)
- Java教程和Android教程列表
翻譯自: https://www.javacodegeeks.com/2011/10/concurrency-optimization-reduce-lock.html
總結(jié)
以上是生活随笔為你收集整理的并发优化–减少锁粒度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米路由器怎么打开小米路由器如何出场
- 下一篇: 在J2SE应用程序中模拟CDI的会话和请