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

 
时间: 2024-10-16 03:07:42

ConcurrentHashMap使用要点的相关文章

Java8 中 ConcurrentHashMap工作原理的要点分析

简介: 本文主要介绍Java8中的并发容器ConcurrentHashMap的工作原理,和其它文章不同的是,本文重点分析了不同线程的各类并发操作如get,put,remove之间是如何同步的,以及这些操作和扩容操作之间同步可能出现的各种情况.由于源代码的分析肯定会有所纰漏,希望大家积极指出错误. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1.Java8中 ConcurrentHashMap的结构 图片来源(http://www

Java中ConcurrentHashMap的实现

Java中ConcurrentHashMap的实现 ConcurrentHashMap(简写CHM)引入了分割,并提供了HashTable支持的所有的功能.在CHM中,支持多线程对Map做读操作,并且不需要任何的blocking.这得益于CHM将Map分割成了不同的部分,在执行更新操作时只锁住一部分.根据默认的并发级别(concurrency level),Map被分割成16个部分,并且由不同的锁控制.这意味着,同时最多可以有16个写线程操作Map.试想一下,由只能一个线程进入变成同时可由16个

ConcurrentHashMap基于JDK1.8源码剖析

前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedHashMap就这么简单[源码剖析] TreeMap就这么简单[源码剖析] 本篇主要讲解ConCurrentHashMap~ 看这篇文章之前最好是有点数据结构的基础: Java实现单向链表 栈和队列就是这么简单 二叉树就这么简单 当然了,如果讲得有错的地方还请大家多多包涵并不吝在评论去指正- 一.Co

ConcurrentHashMap 源码阅读小结

前言 每一次总结都意味着重新开始,同时也是为了更好的开始.ConcurrentHashMap 一直是我心中的痛.虽然不敢说完全读懂了,但也看了几个重要的方法,有不少我觉得比较重要的知识点. 然后呢,放一些楼主写的关于 ConcurrentHashMap 相关源码分析的文章链接: ConcurrentHashMap 扩容分析拾遗 并发编程--ConcurrentHashMap#addCount() 分析 并发编程--ConcurrentHashMap#transfer() 扩容逐行分析 并发编程-

并发编程——ConcurrentHashMap#addCount() 分析

前言 ConcurrentHashMap 精华代码很多,前面分析了 helpTransfer 和 transfer 和 putVal 方法,今天来分析一下 addCount 方法,该方法会在 putVal 方法中调用. 该方法可以配合 size 方法一起查看,关于该方法,楼主也写了一篇文章分析过:并发编程 -- ConcurrentHashMap size 方法原理分析 具体代码如下: addCount(1L, binCount); return null; 当插入结束的时候,会调用该方法,并传

面试流程要点

在上周,我密集面试了若干位Java后端的候选人,工作经验在3到5年间.我的标准其实不复杂:第一能干活,第二Java基础要好,第三最好熟悉些分布式框架,我相信其它公司招初级开发时,应该也照着这个标准来面的. 我也知道,不少候选人能力其实不差,但面试时没准备或不会说,这样的人可能在进团队干活后确实能达到期望,但可能就无法通过面试,但面试官总是只根据面试情况来判断. 但现实情况是,大多数人可能面试前没准备,或准备方法不得当.要知道,我们平时干活更偏重于业务,不可能大量接触到算法,数据结构,底层代码这类

ConcurrentHashMap 的实现原理

概述 我们在之前的博文中了解到关于 HashMap 和 Hashtable 这两种集合.其中 HashMap 是非线程安全的,当我们只有一个线程在使用 HashMap 的时候,自然不会有问题,但如果涉及到多个线程,并且有读有写的过程中,HashMap 就不能满足我们的需要了(fail-fast).在不考虑性能问题的时候,我们的解决方案有 Hashtable 或者Collections.synchronizedMap(hashMap),这两种方式基本都是对整个 hash 表结构做锁定操作的,这样在

javascript必须知道的知识要点(二)

该文章不详细叙述各知识要点的具体内容,仅把要点列出来,供大家学习的时候参照,或者检测自己是否熟练掌握了javascript,清楚各个部分的内容. 内建对象可划分为数据封装类对象.工具类对象.错误类对象,如下. 数据封装类对象: Number对象 String对象 Boolean对象 Array对象 Object Function 工具类对象: Math对象 Date对象 RegExp对象 错误类对象: Error对象  Number对象 JavaScript 只有一种数字类型.Number,包括

Collections.synchronizedMap()与ConcurrentHashMap的区别

前面文章提到Collections.synchronizedMap()与ConcurrentHashM两者都提供了线程同步的功能.那两者的区别在哪呢?我们们先来看到代码例子.    下面代码实现一个线程对map进行写操作,另一个线程,读出并打印map数据. [java] view plain copy package test.map; import java.util.Collections; import java.util.HashMap; import java.util.Hashtab