kxbin
          成功留給有準(zhǔn)備的人
          posts - 10,  comments - 35,  trackbacks - 0

          大家使用多線程無非是為了提高性能,但如果多線程使用不當(dāng),不但性能提升不明顯,而且會(huì)使得資源消耗更大。下面列舉一下可能會(huì)造成多線程性能問題的點(diǎn):

          • 死鎖
          • 過多串行化
          • 過多鎖競(jìng)爭(zhēng)
          • 切換上下文
          • 內(nèi)存同步

          下面分別解析以上性能隱患

          死鎖

          關(guān)于死鎖,我們?cè)趯W(xué)習(xí)操作系統(tǒng)的時(shí)候就知道它產(chǎn)生的原因和危害,這里就不從原理上去累述了,可以從下面的代碼和圖示重溫一下死鎖產(chǎn)生的原因:

           

          1. public class LeftRightDeadlock {  
          2.     private final Object left = new Object();  
          3.     private final Object right = new Object();  
          4.     public void leftRight() {  
          5.         synchronized (left) {  
          6.             synchronized (right) {  
          7.                 doSomething();  
          8.             }  
          9.         }  
          10.     }  
          11.     public void rightLeft() {  
          12.         synchronized (right) {  
          13.             synchronized (left) {  
          14.                 doSomethingElse();  
          15.             }  
          16.         }  
          17.     }  
          18. }  

           

          預(yù)防和處理死鎖的方法:

          1)盡量不要在釋放鎖之前競(jìng)爭(zhēng)其他鎖

          一般可以通過細(xì)化同步方法來實(shí)現(xiàn),只在真正需要保護(hù)共享資源的地方去拿鎖,并盡快釋放鎖,這樣可以有效降低在同步方法里調(diào)用其他同步方法的情況

          2)順序索取鎖資源

          如果實(shí)在無法避免嵌套索取鎖資源,則需要制定一個(gè)索取鎖資源的策略,先規(guī)劃好有哪些鎖,然后各個(gè)線程按照一個(gè)順序去索取,不要出現(xiàn)上面那個(gè)例子中不同順序,這樣就會(huì)有潛在的死鎖問題

          3)嘗試定時(shí)鎖

          Java 5提供了更靈活的鎖工具,可以顯式地索取和釋放鎖。那么在索取鎖的時(shí)候可以設(shè)定一個(gè)超時(shí)時(shí)間,如果超過這個(gè)時(shí)間還沒索取到鎖,則不會(huì)繼續(xù)堵塞而是放棄此次任務(wù),示例代碼如下:

           

          1. public boolean trySendOnSharedLine(String message,  
          2.                                    long timeout, TimeUnit unit)  
          3.                                    throws InterruptedException {  
          4.     long nanosToLock = unit.toNanos(timeout)  
          5.                      - estimatedNanosToSend(message);  
          6.     if (!lock.tryLock(nanosToLock, NANOSECONDS))  
          7.         return false;  
          8.     try {  
          9.         return sendOnSharedLine(message);  
          10.     } finally {  
          11.         lock.unlock();  
          12.     }  
          13. }  

           

          這樣可以有效打破死鎖條件。

          4)檢查死鎖

          JVM采用thread dump的方式來識(shí)別死鎖的方式,可以通過操作系統(tǒng)的命令來向JVM發(fā)送thread dump的信號(hào),這樣可以查詢哪些線程死鎖。

          過多串行化

          用多線程實(shí)際上就是想并行地做事情,但這些事情由于某些依賴性必須串行工作,導(dǎo)致很多環(huán)節(jié)得串行化,這實(shí)際上很局限系統(tǒng)的可擴(kuò)展性,就算加CPU加線程,但性能卻沒有線性增長。有個(gè)Amdahl定理可以說明這個(gè)問題:

          其中,F(xiàn)是串行化比例,N是處理器數(shù)量,由上可知,只有盡可能減少串行化,才能最大化地提高可擴(kuò)展能力。降低串行化的關(guān)鍵就是降低鎖競(jìng)爭(zhēng),當(dāng)很多并行任務(wù)掛在鎖的獲取上,就是串行化的表現(xiàn)

          過多鎖競(jìng)爭(zhēng)

          過多鎖競(jìng)爭(zhēng)的危害是不言而喻的,那么看看有哪些辦法來降低鎖競(jìng)爭(zhēng)

          1)縮小鎖的范圍

          前面也談到這一點(diǎn),盡量縮小鎖保護(hù)的范圍,快進(jìn)快出,因此盡量不要直接在方法上使用synchronized關(guān)鍵字,而只是在真正需要線程安全保護(hù)的地方使用

          2)減小鎖的粒度

          Java 5提供了顯式鎖后,可以更為靈活的來保護(hù)共享變量。synchronized關(guān)鍵字(用在方法上)是默認(rèn)把整個(gè)對(duì)象作為鎖,實(shí)際上很多時(shí)候沒有必要用這么大一個(gè)鎖,這會(huì)導(dǎo)致這個(gè)類所有synchronized都得串行執(zhí)行??梢愿鶕?jù)真正需要保護(hù)的共享變量作為鎖,也可以使用更為精細(xì)的策略,目的就是要在真正需要串行的時(shí)候串行,舉一個(gè)例子:

           

          1. public class StripedMap {  
          2.     // Synchronization policy: buckets[n] guarded by locks[n%N_LOCKS]  
          3.     private static final int N_LOCKS = 16;  
          4.     private final Node[] buckets;  
          5.     private final Object[] locks;  
          6.     private static class Node { ... }  
          7.     public StripedMap(int numBuckets) {  
          8.         buckets = new Node[numBuckets];  
          9.         locks = new Object[N_LOCKS];  
          10.         for (int i = 0; i < N_LOCKS; i++)  
          11.             locks[i] = new Object();  
          12.     }  
          13.     private final int hash(Object key) {  
          14.         return Math.abs(key.hashCode() % buckets.length);  
          15.     }  
          16.     public Object get(Object key) {  
          17.         int hash = hash(key);  
          18.         synchronized (locks[hash % N_LOCKS]) {  
          19.             for (Node m = buckets[hash]; m != null; m = m.next)  
          20.                 if (m.key.equals(key))  
          21.                     return m.value;  
          22.         }  
          23.         return null;  
          24.     }  
          25.     public void clear() {  
          26.         for (int i = 0; i < buckets.length; i++) {  
          27.             synchronized (locks[i % N_LOCKS]) {  
          28.                 buckets[i] = null;  
          29.             }  
          30.         }  
          31.     }  
          32.     ...  
          33. }  

           

          上面這個(gè)例子是通過hash算法來把存取的值所對(duì)應(yīng)的hash值來作為鎖,這樣就只需要對(duì)hash值相同的對(duì)象存取串行化,而不是像HashTable那樣對(duì)任何對(duì)象任何操作都串行化。

          3)減少共享資源的依賴

          共享資源是競(jìng)爭(zhēng)鎖的源頭,在多線程開發(fā)中盡量減少對(duì)共享資源的依賴,比如對(duì)象池的技術(shù)應(yīng)該慎重考慮,新的JVM對(duì)新建對(duì)象以做了足夠的優(yōu)化,性能非常好,如果用對(duì)象池不但不能提高多少性能,反而會(huì)因?yàn)殒i競(jìng)爭(zhēng)導(dǎo)致降低線程的可并發(fā)性。

          4)使用讀寫分離鎖來替換獨(dú)占鎖

          Java 5提供了一個(gè)讀寫分離鎖(ReadWriteLock)來實(shí)現(xiàn)讀-讀并發(fā),讀-寫串行,寫-寫串行的特性。這種方式更進(jìn)一步提高了可并發(fā)性,因?yàn)橛行﹫?chǎng)景大部分是讀操作,因此沒必要串行工作。關(guān)于ReadWriteLock的具體使用可以參加一下示例:

           

          1. public class ReadWriteMap<K,V> {  
          2.     private final Map<K,V> map;  
          3.     private final ReadWriteLock lock = new ReentrantReadWriteLock();  
          4.     private final Lock r = lock.readLock();  
          5.     private final Lock w = lock.writeLock();  
          6.     public ReadWriteMap(Map<K,V> map) {  
          7.         this.map = map;  
          8.     }  
          9.     public V put(K key, V value) {  
          10.         w.lock();  
          11.         try {  
          12.             return map.put(key, value);  
          13.         } finally {  
          14.             w.unlock();  
          15.         }  
          16.     }  
          17.     // Do the same for remove(), putAll(), clear()  
          18.     public V get(Object key) {  
          19.         r.lock();  
          20.         try {  
          21.             return map.get(key);  
          22.         } finally {  
          23.             r.unlock();  
          24.         }  
          25.     }  
          26.     // Do the same for other read-only Map methods  
          27. }  

           

          切換上下文

          線程比較多的時(shí)候,操作系統(tǒng)切換線程上下文的性能消耗是不能忽略的,在構(gòu)建高性能web之路------web服務(wù)器長連接 可以看出在進(jìn)程切換上的代價(jià),當(dāng)然線程會(huì)更輕量一些,不過道理是類似的

          內(nèi)存同步

          當(dāng)使用到synchronized、volatile或Lock的時(shí)候,都會(huì)為了保證可見性導(dǎo)致更多的內(nèi)存同步,這就無法享受到JMM結(jié)構(gòu)帶來了性能優(yōu)化。

          posted on 2011-10-13 16:04 kxbin 閱讀(329) 評(píng)論(0)  編輯  收藏 所屬分類: java基礎(chǔ)
          你恨一個(gè)人是因?yàn)槟銗鬯荒阆矚g一個(gè)人,是因?yàn)樗砩嫌心銢]有的;你討厭一個(gè)人是因?yàn)樗砩嫌心阌械臇|西;你經(jīng)常在別人面前批評(píng)某人,其實(shí)潛意識(shí)中是想接近他。

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(5)

          隨筆檔案

          文章分類

          文章檔案

          相冊(cè)

          收藏夾

          J2EE

          java技術(shù)網(wǎng)站

          Linux

          平時(shí)常去的網(wǎng)站

          數(shù)據(jù)庫

          電影網(wǎng)站

          網(wǎng)站設(shè)計(jì)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 林甸县| 利川市| 甘谷县| 监利县| 安吉县| 宝山区| 资溪县| 米脂县| 蒙阴县| 古蔺县| 镇远县| 北辰区| 泰来县| 石狮市| 玉田县| 柳林县| 高清| 达日县| 桑植县| 大方县| 崇左市| 莲花县| 香港| 平山县| 九寨沟县| 大埔县| 墨江| 宜春市| 沧源| 德清县| 鹤壁市| 台南县| 平湖市| 新田县| 通海县| 罗城| 唐山市| 无极县| 三河市| 运城市| 周宁县|