ConcurrentHashMap使用要點


ConcurrentHashMap的簡要總結:

1、public V get(Object key)不涉及到鎖,也就是說獲得對象時沒有使用鎖;

2、put、remove方法要使用鎖,但並不一定有鎖爭用,原因在於ConcurrentHashMap將緩存的變量分到多個Segment,每個Segment上有一個鎖,只要多個線程訪問的不是一個Segment就沒有鎖爭用,就沒有堵塞,各線程用各自的鎖,ConcurrentHashMap缺省情況下生成16個Segment,也就是允許16個線程並發的更新而盡量沒有鎖爭用;

3、Iterator對象的使用,不一定是和其它更新線程同步,獲得的對象可能是更新前的對象,ConcurrentHashMap允許一邊更新、一邊遍歷,也就是說在Iterator對象遍歷的時候,ConcurrentHashMap也可以進行remove,put操作,且遍歷的數據會隨着remove,put操作產出變化,所以希望遍歷到當前全部數據的話,要么以ConcurrentHashMap變量為鎖進行同步(synchronized該變量),要么使用CopiedIterator包裝iterator,使其拷貝當前集合的全部數據,但是這樣生成的iterator不可以進行remove操作。

 

Hashtable和ConcurrentHashMap的不同點:

1、Hashtable對get,put,remove都使用了同步操作,它的同步級別是正對Hashtable來進行同步的,也就是說如果有線程正在遍歷集合,其他的線程就暫時不能使用該集合了,這樣無疑就很容易對性能和吞吐量造成影響,從而形成單點。而ConcurrentHashMap則不同,它只對put,remove操作使用了同步操作,get操作並不影響,詳情請看以上第1,2點,當前ConcurrentHashMap這樣的做法對一些線程要求很嚴格的程序來說,還是有所欠缺的,對應這樣的程序來說,如果不考慮性能和吞吐量問題的話,個人覺得使用Hashtable還是比較合適的;

2、Hashtable在使用iterator遍歷的時候,如果其他線程,包括本線程對Hashtable進行了put,remove等更新操作的話,就會拋出ConcurrentModificationException異常,但如果使用ConcurrentHashMap的話,就不用考慮這方面的問題了,詳情請看以上第3點;

 

1.HashMap或者ArrayList邊遍歷邊刪除數據會報java.util.ConcurrentModificationException異常

Map<Long, String> mReqPacket = new HashMap<Long, String>();
        for (long i = 0; i < 15; i++) {
            mReqPacket.put(i, i + "");
        }

        for (Entry<Long, String> entry : mReqPacket.entrySet()) {
            long key = entry.getKey();
            String value = entry.getValue();
            if (key < 10) {
                mReqPacket.remove(key);
            }
        }

        for (Entry<Long, String> entry : mReqPacket.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }

所以要用迭代器刪除元素:

        Map<Long, String> mReqPacket = new HashMap<Long, String>();
        for (long i = 0; i < 15; i++) {
            mReqPacket.put(i, i + "");
        }

        for (Iterator<Entry<Long, String>> iterator = mReqPacket.entrySet().iterator(); iterator.hasNext();) {
            Entry<Long, String> entry = iterator.next();
            long key = entry.getKey();
            if (key < 10) {
                iterator.remove();
            }
        }

        for (Entry<Long, String> entry : mReqPacket.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }

 

2.對ConcurrentHashMap邊遍歷邊刪除或者增加操作不會產生異常(可以不用迭代方式刪除元素),因為其內部已經做了維護,遍歷的時候都能獲得最新的值。即便是多個線程一起刪除、添加元素也沒問題。

        Map<Long, String> conMap = new ConcurrentHashMap<Long, String>();
        for (long i = 0; i < 15; i++) {
            conMap.put(i, i + "");
        }

        for (Entry<Long, String> entry : conMap.entrySet()) {
            long key = entry.getKey();
            if (key < 10) {
                conMap.remove(key);
            }
        }

        for (Entry<Long, String> entry : conMap.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }

 

3.一個線程對ConcurrentHashMap增加數據,另外一個線程在遍歷時就能獲得。

static Map<Long, String> conMap = new ConcurrentHashMap<Long, String>();

    public static void main(String[] args) throws InterruptedException {
        for (long i = 0; i < 5; i++) {
            conMap.put(i, i + "");
        }

        Thread thread = new Thread(new Runnable() {
            public void run() {
                conMap.put(100l, "100");
                System.out.println("ADD:" + 100);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                for (Iterator<Entry<Long, String>> iterator = conMap.entrySet().iterator(); iterator.hasNext();) {
                    Entry<Long, String> entry = iterator.next();
                    System.out.println(entry.getKey() + " - " + entry.getValue());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
        thread2.start();

        Thread.sleep(3000);
        System.out.println("--------");
        for (Entry<Long, String> entry : conMap.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }

    }

輸出:

ADD:100
0 - 0
100 - 100
2 - 2
1 - 1
3 - 3
4 - 4
--------
0 0
100 100
2 2
1 1
3 3
4 4

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM