分段鎖——ConcurrentHashMap

一、線程不安全的HashMap
由於多線程環境下,使用Hashmap進行put操做會引發死循環,致使CPU利用率接近100%,因此在併發狀況下不能使用HashMap。java

二、效率低下的HashTable容器
HashTable容器使用synchronized來保證線程安全,但在線程競爭激烈的狀況下HashTable的效率很是低下。由於當一個線程訪問HashTable的同步方法時,其餘線程訪問HashTable的同步方法時,可能會進入阻塞或輪詢狀態。如線程1使用put進行添加元素,線程2不但不能使用put方法添加元素,而且也不能使用get方法來獲取元素,因此競爭越激烈效率越低。數組

三、ConcurrentHashMap分段鎖技術
ConcurrentHashMap是Java 5中支持高併發、高吞吐量的線程安全HashMap實現。安全

咱們以ConcurrentHashMap來講一下分段鎖的含義以及設計思想,ConcurrentHashMap中的分段鎖稱爲Segment,相似於HashMap(JDK7與JDK8中HashMap的實現)的結構,即內部擁有一個Entry數組,數組中的每一個元素又是一個鏈表;同時又是一個ReentrantLock(Segment繼承了ReentrantLock)。服務器

當須要put元素的時候,並非對整個hashmap進行加鎖,而是先經過hashcode來知道他要放在哪個分段中,而後對這個分段進行加鎖,因此當多線程put的時候,只要不是放在一個分段中,就實現了真正的並行的插入。多線程

可是,在統計size的時候,可就是獲取hashmap全局信息的時候,就須要獲取全部的分段鎖才能統計。併發

分段鎖的設計目的是細化鎖的粒度,當操做不須要更新整個數組的時候,就僅僅針對數組中的一項進行加鎖操做。ide

四、用法
模擬信息的發送和接收,場景是這樣的:
服務器向客戶端發送信息,要保證信息100%的發送給客戶端,那麼發給客戶端以後,客戶端返回一個消息告訴服務器,已經收到。當服務器一直沒有收到客戶端返回的消息,那麼服務器會一直髮送這個信息,直到客戶端接收並確認該信息,這時候再刪除重複發送的這個信息。高併發

爲了模擬該場景,這裏寫兩個線程,一個是發送線程,一個是接收線程,把要發送的信息保存到線程安全的對象裏面,防止發生線程安全問題,這裏採用ConcurrentHashMap。spa

a、SendThread:信息從新發送類線程

package com.lynch.lock; import java.util.Map.Entry; /** * * * @author Lynch */
public class SendThread extends Thread { @Override public void run() { try { sleep(6000); while (ConcurrentHashMapTest.pushMessage.size() > 0) { for (Entry<Integer, String> hashMap : ConcurrentHashMapTest.pushMessage.entrySet()) { System.out.println("消息id:" + hashMap.getKey()+ "未發送成功,在此重發:" + hashMap.getValue()); } sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }

b、ReceiveThread:信息接收類

package com.lynch.lock; import java.util.Map.Entry; /** * 接收發送過來的信息,並從內存中刪除 * * @author Administrator * */
public class ReceiveThread extends Thread { @Override public void run() { try { for (int i = 0; i < 100000; i++) { sleep(2000); for (Entry<Integer, String> map : ConcurrentHashMapTest.pushMessage.entrySet()) { if (map.getKey() == i) { System.out.println("成功收到id爲:" + map.getKey() + "返回的信息,刪除該元素"); ConcurrentHashMapTest.pushMessage.remove(map.getKey()); } } System.out.println("內存對象中的元素數量爲:" + ConcurrentHashMapTest.pushMessage.size()); } } catch (InterruptedException e) { e.printStackTrace(); } } }

 

c、ConcurrentHashMapTest:主類入口

package com.lynch.lock; import java.util.concurrent.ConcurrentHashMap; /** * 模擬信息的發送和接收 * * @author Lynch */
public class ConcurrentHashMapTest { public static ConcurrentHashMap<Integer, String> pushMessage = new ConcurrentHashMap<Integer, String>(); public static void main(String[] args) { for (int i = 0; i < 5; i++) { pushMessage.put(i, "該消息是id爲" + i + "的消息"); } Thread sendThread = new SendThread(); Thread receiveThread = new ReceiveThread(); sendThread.start(); receiveThread.start(); for (int i = 5; i < 10; i++) { pushMessage.put(i, "該消息是id爲" + i + "的消息"); } } }

這樣兩個線程能夠輪流的進行各自的事情,而且不會形成數據安全的問題。用這種方式,再結合Androidpn的推送機制,會更加符合實際生產中的應用。

相關文章
相關標籤/搜索