1、Collections.synchronizedMap()

实现上在调用map所有方法时,都对整个map进行同步,而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。这样,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。

ConcurrentHashMap从类的命名就能看出,它必然是个HashMap。Collections.synchronizedMap()可以接收任意Map实例作为封装。

=====================================================================================

下面的例子显示java.util.Collections.synchronizedMap()方法的使用

package com.yiibai;

import java.util.*;

public class CollectionsDemo {

public static void main(String[] args) {

// create map

Map<String,String> map = new HashMap<String,String>();

// populate the map

map.put("1","TP");

map.put("2","IS");

map.put("3","BEST");

// create a synchronized map

Map<String,String> synmap = Collections.synchronizedMap(map);

System.out.println("Synchronized map is :"+synmap);

}

}

现在编译和运行上面的代码示例,将产生以下结果。

Synchronized map is :{3=BEST, 2=IS, 1=TP}

也不会抛出ConcurrentModificationException。

===============================================================================

2、ConcurrentHashMap

1:线程安全,

2:Iterator的迭代方式采用的是fail-safe的错误机制

ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求,ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响.

3:而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。

4:同时,同步操作精确控制到桶,所以,即使在遍历map时,其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。

ConcurrentHashMap采用了分段锁的设计,只有在同一个分段内才存在竞态关系,不同的分段锁之间没有锁竞争。相比于对整个Map加锁的设计,分段锁大大的提高了高并发环境下的处理能力。但同时,由于不是对整个Map加锁,导致一些需要扫描整个Map的方法(如size(), containsValue())需要使用特殊的实现,另外一些方法(如clear())甚至放弃了对一致性的要求(ConcurrentHashMap是弱一致性的,

ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

源代码:

static final class Segment<K,V> extends ReentrantLock implements Serializable{

并发度

并发度可以理解为程序运行时能够同时更新ConccurentHashMap且不产生锁竞争的最大线程数,实际上就是ConcurrentHashMap中的分段锁个数,即Segment[]的数组长度。ConcurrentHashMap默认的并发度为16,但用户也可以在构造函数中设置并发度。当用户设置并发度时,ConcurrentHashMap会使用大于等于该值的最小2幂指数作为实际并发度(假如用户设置并发度为17,实际并发度则为32)。运行时通过将key的高n位(n = 32 – segmentShift)和并发度减1(segmentMask)做位与运算定位到所在的Segment。segmentShift与segmentMask都是在构造过程中根据concurrency level被相应的计算出来。

如果并发度设置的过小,会带来严重的锁竞争问题;如果并发度设置的过大,原本位于同一个Segment内的访问会扩散到不同的Segment中,CPU cache命中率会下降,从而引起程序性能下降。(文档的说法是根据你并发的线程数量决定,太多会导性能降低)

JDK8中的实现

ConcurrentHashMap在JDK8中进行了巨大改动,很需要通过源码来再次学习下Doug Lea的实现方法。

它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。它沿用了与它同时期的HashMap版本的思想,底层依然由"数组"+链表+红黑树的方式思想(JDK7与JDK8中HashMap的实现),但是为了做到并发,又增加了很多辅助的类,例如TreeBin,Traverser等对象内部类。

重要的属性

首先来看几个重要的属性,与HashMap相同的就不再介绍了,这里重点解释一下sizeCtl这个属性。可以说它是ConcurrentHashMap中出镜率很高的一个属性,因为它是一个控制标识符,在不同的地方有不同用途,而且它的取值不同,也代表不同的含义。

  • 负数代表正在进行初始化或扩容操作
  • -1代表正在初始化
  • -N 表示有N-1个线程正在进行扩容操作
  • 正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小,这一点类似于扩容阈值的概念。还后面可以看到,它的值始终是当前ConcurrentHashMap容量的0.75倍,这与loadfactor是对应的。

总结

JDK6,7中的ConcurrentHashmap主要使用Segment来实现减小锁粒度,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。

jdk7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能,所以jdk8 中完全重写了concurrentHashmap,代码量从原来的1000多行变成了 6000多 行,实现上也和原来的分段式存储有很大的区别。

主要设计上的变化有以下几点:

  1. 不采用segment而采用node,锁住node来实现减小锁粒度。
  2. 设计了MOVED状态 当resize的中过程中 线程2还在put数据,线程2会帮助resize。
  3. 使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。
  4. sizeCtl的不同值来代表不同含义,起到了控制的作用。

至于为什么JDK8中使用synchronized而不是ReentrantLock,我猜是因为JDK8中对synchronized有了足够的优化吧。

3、LinkedHashMap

是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

Map<Integer,String> map = new LinkedHashMap<Integer,String>();

map.put(6, "apple");

map.put(3, "banana");

map.put(2,"pear");

for (Iterator it =  map.keySet().iterator();it.hasNext();)

{

Object key = it.next();

System.out.println( key+"="+ map.get(key));

}

运行结果如下:

*************************LinkedHashMap*************

6=apple

3=banana

2=pear

4、TreeMap

1:TreeMap 类不仅实现了 Map 接口,还实现了 Map 接口的子接口 java.util.SortedMap。

2:TreeMap 类中不允许键对象为 null 或是基本数据类型,这是因为 TreeMap 中的对象必须是可排序的(即对象需要实现 java.lang.Comparable 接口)

3:在创建 TreeMap 对象时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序;如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。

4:在添加、删除和定位映射关系上,TreeMap类要比HashMap类的性能差一些,

5:在需要排序时,利用现有的 HashMap,创建一个 TreeMap 类型的实例

Java代码  

  1. import java.util.Collections;  
  2. import java.util.HashMap;  
  3. import java.util.Iterator;  
  4. import java.util.Map;  
  5. import java.util.TreeMap;  
  6. public class TestCollection {  
  7. public static void main(String[] args) {
  8. System.out.println("开始:");
  9. Person person1 = new Person("马先生", 220181);
  10. Person person2 = new Person("李先生", 220193);
  11. Person person3 = new Person("王小姐", 220186);
  12. Map<Number, Person> map = new HashMap<Number, Person>();
  13. map.put(person1.getId_card(), person1);
  14. map.put(person2.getId_card(), person2);
  15. map.put(person3.getId_card(), person3);
  16. // HashMap
  17. System.out.println("HashMap,无序:");
  18. for (Iterator<Number> it = map.keySet().iterator(); it.hasNext();) {
  19. Person person = map.get(it.next());
  20. System.out.println(person.getId_card() + " " + person.getName());
  21. }
  22. // TreeMap
  23. System.out.println("TreeMap,升序:");
  24. //创建 TreeMap 时,如果使用参数为空的构造方法,则根据 Map 对象的 key 进行排序
  25. TreeMap<Number, Person> treeMap = new TreeMap<Number, Person>();
  26. //将指定映射中的所有映射关系复制到此映射中。
  27. treeMap.putAll(map);           for (Iterator<Number> it = treeMap.keySet().iterator(); it.hasNext();) {
  28. Person person = treeMap.get(it.next());
  29. System.out.println(person.getId_card() + " " + person.getName());
  30. }
  31. System.out.println("TreeMap,降序:");
  32. //Collections.reverseOrder()返回一个比较器,它强行逆转指定比较器顺序
  33. //如果使用参数为 Comparator 的构造方法,则根据 Comparator 进行排序。
  34. TreeMap<Number, Person> treeMap2 =
  35. new TreeMap<Number, Person>(Collections.reverseOrder());
  36. treeMap2.putAll(map);
  37. for (Iterator it = treeMap2.keySet().iterator(); it.hasNext();) {
  38. Person person = (Person) treeMap2.get(it.next());
  39. System.out.println(person.getId_card() + " " + person.getName());
  40. }
  41. System.out.println("结束!");
  42. }
  43. }

集合框架—常用的map集合的更多相关文章

  1. 牛客网Java刷题知识点之Java 集合框架的构成、集合框架中的迭代器Iterator、集合框架中的集合接口Collection(List和Set)、集合框架中的Map集合

    不多说,直接上干货! 集合框架中包含了大量集合接口.这些接口的实现类和操作它们的算法. 集合容器因为内部的数据结构不同,有多种具体容器. 不断的向上抽取,就形成了集合框架. Map是一次添加一对元素. ...

  2. 理解java集合——集合框架 Collection、Map

    1.概述: @white Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称对象)"丢进"该容器中. 2.Java集合大致可以分4类: @white Set ...

  3. 07java进阶——集合框架3(Map)

    1.映射表(Map) 1.1基本概念 1.2Map中常用的方法 package cn.jxufe.java.chapter7; import java.util.HashMap; import jav ...

  4. 浅谈集合框架三、Map常用方法及常用工具类

    最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...

  5. Java集合框架List,Map,Set等全面介绍

    Java集合框架的基本接口/类层次结构: java.util.Collection [I]+--java.util.List [I]   +--java.util.ArrayList [C]   +- ...

  6. 【转】Java集合框架List,Map,Set等全面介绍

    原文网址:http://android.blog.51cto.com/268543/400557 Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含 ...

  7. java的集合框架set 和map的深入理解

    Java的集合框架之Map的用法详解 Map有两种比较常用的实现:HashMap 和 TreeMap. HashMap: HashMap 也是无序的,也是按照哈希编码来排序的,允许使用null 值和n ...

  8. 34、Java集合框架List,Map,Set等全面介绍(转载)

      Java Collections Framework是Java提供的对集合进行定义,操作,和管理的包含一组接口,类的体系结构.   Java集合框架的基本接口/类层次结构: java.util.C ...

  9. java学习——集合框架(泛型,Map)

    泛型: ... Map:一次添加一对元素.Collection 一次添加一个元素. Map也称为双列集合,Collection集合称为单列集合. 其实map集合中存储的就是键值对. map集合中必须保 ...

随机推荐

  1. myisam innodb 次级 索引的区别

    MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址.下图是MyISAM索引的原理图: 这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索 ...

  2. Python拷贝文件脚本

    author : headsen chen date : 2018-12-06  17:56:58 copy_file.py #!/usr/bin/env python from sys import ...

  3. iOS8跳转到系统设置页

    版权声明:本文为博主原创文章,未经博主允许不得转载. 大家都知道,在iOS5.0时时可以跳转到系统的设置页的.但是在5.1之后就不可以了. 刚才研究了下这个问题,发现只有iOS8可以跳转到系统设置里自 ...

  4. jenkins中windows节点设置开机自启动slave-agent

    做web UI自动化时,为了提高效率,用了多台windows节点来跑自动化,但slave-agent每次在关机后都得手工启动,麻烦,网上看到了一系列说启动任务中,感觉还是不考虑,这里使用windows ...

  5. unity3d的优化场景技术LOD+IOC

    一.unity3d的优化场景技术  LOD+IOC 遮挡剔除(occlusion culling)其实就是在摄像机范围内的物体才被渲染出来,没有在视野范围内的,统统关掉渲染,这样能让性能大大提高. I ...

  6. github相关资料记录

    github官方配ssh api:https://help.github.com/articles/generating-ssh-keys 简书hexo静态博客搭建:http://www.jiansh ...

  7. C++ 标准输出cout与printf

    C++标准输出cout与printf都可以,printf用法更死板一些. #include <iostream> int main(int argc, char** argv) { usi ...

  8. mui+vue+photoclip做APP头像裁剪上传

    做APP由于项目需要,需要做用户头像上传的功能,头像上传包括拍照和相册选择图片进行上传,这里使用的技术是mui封装的plus,在进行图片裁剪的时候,使用的是photoclip来进行裁剪,由于个人在使用 ...

  9. js模拟点击打开超链接

    js模拟点击打开超链接,页面上有一些锚文本,如果用 JS 批量在新窗口打开. jquery示例: <div class="link"> <a href=" ...

  10. 解决 Python shell 中 Delete/Backspace 键乱码问题

    简述 进入 Python shell,按下 Delete/Backspace 键,会出现 ^H 字符.命令输入错误后只能从头开始,无法删除,让人很头疼.为了便于后期使用,分享一个一劳永逸的方式. 基本 ...