Chinaunix首页 | 论坛 | 博客
  • 博客访问: 69217
  • 博文数量: 43
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-27 15:04
个人简介

记录,分享

文章分类

全部博文(43)

文章存档

2017年(24)

2015年(1)

2014年(18)

我的朋友

分类: Java

2017-03-16 12:10:04

[ ConcurrentMap ]

|- ConcurrentHashMap

|- ConcurrentSkipListMap

一、ConcurrentMap 主要方法

只有以下四个方法,能够保证是原子操作的。 ConcurrentHashMapConcurrentSkipListMap的其他方法无法保证原子性。

1. V putIfAbsent(K key,V value)

如果不存在key对应的值,则将valuekey加入Map,并返回null,否则返回key对应的旧值。相当于

if (!map.containsKey(key))

return map.put(key, value);

else

return map.get(key);

如果该接口实现不允许添加null值(jdk提供的两个类都是如此),可以通过断返回值是否为null验证是否添加成功。

V ret = putIfAbsent(key, value);

if(ret == null){

//sucess

}else{

//fail to add, key exists

}

2. boolean remove(Object key, Object value) :

仅当key-value键值对存在时,才移除该键值。相当于

if (map.containsKey(key) && map.get(key).equals(value)) {

map.remove(key);

return true;

} else return false;

3. V replace(K key, V value)

仅当key存在时,才将value添加进map,否则返回null。与putIfAbsent()相反。相当于

if (map.containsKey(key)) {

return map.put(key, value);

} else return null;

4. boolean replace(K key, V oldValue, V newValue)

仅当key存在并且对应值为oldValue时,才将newValue添加进map,并返回true,否则返回false。相当于

if (map.containsKey(key) && map.get(key).equals(oldValue)) {

map.put(key, newValue);

return true;

} else return false;

二、ConcurrentHashMap实现线程安全的原理

一个ConcurrentHashMap 由多个segment 组成,每个segment 包含一个Entity 的数组。通过hash() 函数得到key 的哈希值,在得到相应的segment,在通过segment 存储Entity。这里比HashMap 多了一个segment 类。该类继承了ReentrantLock 类,所以本身是一个锁。当多线程对ConcurrentHashMap 操作时,不是完全锁住map, 而是锁住相应的segment 。这样提高了并发效率。

缺点:当遍历map 中的元素时,需要获取所有的segment 的锁,使用遍历时慢。锁的增多,占用了系统的资源。使得对整个集合进行操作的一些方法(例如 size() isEmpty() )的实现更加困难,因为这些方法要求一次获得许多的锁,并且还存在返回不正确的结果的风险。

一些细节:

1public V get(Object key)不涉及到锁,也就是说获得对象时没有使用锁;

2keySet().iterator()keys(),获取的IteratorEnumeration变量是单线程访问安全的,多线程访问时要么生成多个IteratorEnumeration(通过调用相应的获取方法),要么以ConcurrentHashMap变量为锁进行同步(synchronized该变量)ConcurrentHashMap变量是多线程访问安全的,尽管是多线程访问,多数情况下应该没有锁争用;

3putremove方法要使用锁,但并不一定有锁争用,原因在于ConcurrentHashMap将缓存的变量分到多个Segment,每个Segment上有一个锁,只要多个线程访问的不是一个Segment就没有锁争用,就没有堵塞,各线程用各自的锁,ConcurrentHashMap缺省情况下生成16Segment,也就是允许16个线程并发的更新而尽量没有锁争用;

4IteratorEnumeration获得的对象,不一定是和其它更新线程同步,获得的对象可能是更新前的对象。ConcurrentHashMap允许一边更新、一边遍历,因为他的遍历器的next()方法, 每次都是返回一个newWriteThroughEntry, 这个东西保证了你在获取到Entry以后即使Map遭到修改, 也不会影响你当前遍历的结果。但是, 如果你对WriteThroughEntry进行setValue操作, 还是可以影响到原来的map的。

5、有些情况下这种不一致是允许的,如果需要最大的性能、吞吐量,则正好使用ConcurrentHashMap

三、ConcurrentSkipListMap 实现线程安全的原理

所有的修改操作都是使用CASUNSAFE.compareAndSwapObject()),只要失败就会重试,直至成功,所以就算多线程并发操作也不会出现错误,而且通过CAS避免了使用锁,性能比用锁好很多。

阅读(522) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~