详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt215

SynchronizedMap和ConcurrentHashMap的深入分析

在开始之前,先介绍下Map是什么?

javadoc中对Map的解释如下:

An objectthat maps keys to values . Amap cannot contain duplicatekeys; each key can map to at most one value.

This interface takes the place of the Dictionary class, which was atotally abstract class rather than an interface.

The Map interface provides three collection views, which allow amap's contents to be viewed as a set of keys, collection of values,or set of key-value mappings.

从上可知,Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。

Map可以使用多种实现方式,HashMap的实现采用的是hash表;而TreeMap采用的是红黑树。

1. Hashtable 和 HashMap

这两个类主要有以下几方面的不同:

Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类。

在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。而在Hashtable中,无论是key还是value都不能为null。

这两个类最大的不同在于Hashtable是线程安全的,它的方法是同步了的,可以直接用 在多线程环境中。而HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。因此,在Collections类中提供了一个方法返回一个 同步版本的HashMap用于多线程的环境:

Java代码

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<K,V>(m);
 }

该方法返回的是一个SynchronizedMap的实例。SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized关键字进行了同步控制。

2. 潜在的线程安全问题

上面提到Collections为HashMap提供了一个并发版本SynchronizedMap。这个版本中的方法都进行了同步,但是这并不等于这个类就一定是线程安全的。在某些时候会出现一些意想不到的结果。

如下面这段代码:

Java代码

// shm是SynchronizedMap的一个实例
if(shm.containsKey('key')){
        shm.remove(key);
}

这段代码用于从map中删除一个元素之前判断是否存在这个元素。这里的 containsKey和reomve方法都是同步的,但是整段代码却不是。考虑这么一个使用场景:线程A执行了containsKey方法返回 true,准备执行remove操作;这时另一个线程B开始执行,同样执行了containsKey方法返回true,并接着执行了remove操作;然 后线程A接着执行remove操作时发现此时已经没有这个元素了。要保证这段代码按我们的意愿工作,一个办法就是对这段代码进行同步控制,但是这么做付出 的代价太大。

在进行迭代时这个问题更改明显。Map集合共提供了三种方式来分别返回键、值、键值对的集合:

Java代码

Set<K> keySet();

Collection<V> values();

Set<Map.Entry<K,V>> entrySet();

在这三个方法的基础上,我们一般通过如下方式访问Map的元素:

Java代码

Iterator keys = map.keySet().iterator();

while(keys.hasNext()){
        map.get(keys.next());
}

在这里,有一个地方需要注意的是:得到的keySet和迭代器都是Map中元素的一个“视图”,而不是“副本”。问题也就出现在这里,当一个线程正在迭代Map中的元素时,另一个线程可能正在修改其中的元素。此时,在迭代元素时就可能会抛出ConcurrentModificationException异常。为了解决这个问题通常有两种方法,一是直接返回元素的副本,而不是视图。这个可以通过

集合类的 toArray()方法实现,但是创建副本的方式效率比之前有所降低,特别是在元素很多的情况下;另一种方法就是在迭代的时候锁住整个集合,这样的话效率就更低了。

3. 更好的选择:ConcurrentHashMap

效率低下的HashTable容器

HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况 下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞 或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

锁分段技术

HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把 锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是 ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据 的时候,其他段的数据也能被其他线程访问。

java5中新增了ConcurrentMap接口和它的一个实现类 ConcurrentHashMap。ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机 制。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是 一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。 这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。

上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。

在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。

SynchronizedMap和ConcurrentHashMap 区别的更多相关文章

  1. Collections.synchronizedMap()与ConcurrentHashMap区别

    Collections.synchronizedMap()与ConcurrentHashMap主要区别是:Collections.synchronizedMap()和Hashtable一样,实现上在调 ...

  2. Collections.synchronizedMap()、ConcurrentHashMap、Hashtable之间的区别

    为什么要比较Hashtable.SynchronizedMap().ConcurrentHashMap之间的关系?因为常用的HashMap是非线程安全的,不能满足在多线程高并发场景下的需求. 那么为什 ...

  3. Collections.synchronizedMap()与ConcurrentHashMap的区别

    前面文章提到Collections.synchronizedMap()与ConcurrentHashM两者都提供了线程同步的功能.那两者的区别在哪呢?我们们先来看到代码例子.    下面代码实现一个线 ...

  4. Java中SynchronizedMap与ConcurrentHashMap的对比

    如何使用 概述 ConcurrentHashMap: 线程安全: 其将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都 ...

  5. Java容器:HashTable, synchronizedMap与ConcurrentHashMap

    首先需要明确的是,不管使用那种Map,都不能保证公共混合调用的线程安全,只能保证单条操作的线程安全,在这一点上各Map不存在优劣. 前文中简单说过HashTable和synchronizedMap,其 ...

  6. Hashtable与ConcurrentHashMap区别

    Hashtable与ConcurrentHashMap区别 ConcurrentHashMap融合了hashtable和hashmap二者的优势. hashtable是做了同步的,是线性安全的,(2) ...

  7. 测试HashTable、Collections.synchronizedMap和ConcurrentHashMap的性能

        对于map的并发操作有HashTable.Collections.synchronizedMap和ConcurrentHashMap三种,到底性能如何呢? 测试代码: package com. ...

  8. Hashtable、synchronizedMap、ConcurrentHashMap 比较

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp18 Hashtable.synchronizedMap.Concurren ...

  9. HashMap、HashTable、ConcurrentHashMap区别

    HashMap与HashTable区别 HashMap与ConcurrentHashMap区别 1.HashMap与HashTable的区别 HashMap线程不安全,HashTable线程安全 Ha ...

随机推荐

  1. year:2017 month:8 day:1+

    2017-08-01 JAVAse 方法的重载:在同一个类中存在一个以上的同名方法,只要他们的参数数量,参数类型,参数顺序(两个相同类型的参数是不行的)这样就构成了方法的重载. 有返回值的方法有三种调 ...

  2. JavaScript字符串处理

    字符串处理 1.连接字符串: 1)连接符+: 2)连接赋值+=: 3)连接函concat() 2.查找子串位置indexOf() 1)在指定字符串中是否存在给定的字符串(第一次出现) 2)用法str. ...

  3. Unicode 与 Unicode Transformation Format(UTF,UTF-8 / UTF-16 / UTF-32)

    ASCII(American Standard Code for Information Interchange):早期它使用7 bits来表示一个字符,总共表示27 = 128个字符:后来扩展到8 ...

  4. vue指令v-cloak示例解析

    v-cloak会隐藏未编译的 Mustache 标签,直至实例准备完毕: [v-cloak] { display: none; } <div v-cloak> {{ message }} ...

  5. ASP.NET Core 源码学习之 Logging[4]:FileProvider

    前面几章介绍了 ASP.NET Core Logging 系统的配置和使用,而对于 Provider ,微软也提供了 Console, Debug, EventSource, TraceSource ...

  6. 再起航,我的学习笔记之JavaScript设计模式06(工厂方法模式)

    上一次已经给大家介绍了简单工厂模式,相信大家对创建型设计模式有了初步的了解,本次我将给大家介绍的是工厂方法模式. 工厂方法模式 工厂方法模式(Factory Method):通过对产品类的抽象使其创建 ...

  7. angularJS实战(一)

    angular实现列表 accessCtrl.js let AccessCtrl = function($scope, AlertService, DialogService, BigDataServ ...

  8. python===基础,修改idle的输入风格

    http://blog.csdn.net/aq_cainiao_aq/article/details/51701861

  9. linux 压缩解压打包工具大集合

    压缩.解压缩及归档工具有很多,今天小编就整理几个大家较为常用的. compress gzip  bzip2 xz zip tar cpio 一.压缩.解压工具 用法 压缩 工具 压缩后 压缩包格式 解 ...

  10. 程序设计中的数学思维函数总结(代码以C#为例)

    最近以C#为例,学习了程序设计基础,其中涉及到一些数学思维,我们可以巧妙的将这些逻辑问题转换为代码,交给计算机运算. 现将经常会使用到的基础函数做一总结,供大家分享.自己备用. 1.判断一个数是否为奇 ...