Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2793161
  • 博文数量: 471
  • 博客积分: 7081
  • 博客等级: 少将
  • 技术积分: 5369
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-04 21:55
文章分类

全部博文(471)

文章存档

2014年(90)

2013年(69)

2012年(312)

分类: Java

2012-07-20 09:41:14

线程安全的Map在JDK 1.5及其更高版本环境 有哪几种方法可以实现? 

1、Map map = Collections.synchronizedMap(new HashMap()); 

点击(此处)折叠或打开

  1. Map map = Collections.synchronizedMap(new HashMap());

    private static class SynchronizedMap implements Map,

  2. Serializable {
  3. // use serialVersionUID from JDK 1.2.2 for interoperability
  4. private static final long serialVersionUID = 1978198479659022715L;

  5. private Map m; // Backing Map
  6. Object mutex; // Object on which to synchronize
  7. SynchronizedMap(Map m) {
  8. if (m == null)
  9. throw new NullPointerException();
  10. this.m = m;
  11. mutex = this;
  12. }
  13. SynchronizedMap(Map m, Object mutex) {
  14. this.m = m;
  15. this.mutex = mutex;
  16. }
  17. public int size() {
  18. synchronized (mutex) {
  19. return m.size();
  20. }
  21. }

  22. public boolean isEmpty() {
  23. synchronized (mutex) {
  24. return m.isEmpty();
  25. }
  26. }

  27. public boolean containsKey(Object key) {
  28. synchronized (mutex) {
  29. return m.containsKey(key);
  30. }
  31. }

  32. public boolean containsValue(Object value) {
  33. synchronized (mutex) {
  34. return m.containsValue(value);
  35. }
  36. }

  37. public V get(Object key) {
  38. synchronized (mutex) {
  39. return m.get(key);
  40. }
  41. }

  42. public V put(K key, V value) {
  43. synchronized (mutex) {
  44. return m.put(key, value);
  45. }
  46. }

  47. public V remove(Object key) {
  48. synchronized (mutex) {
  49. return m.remove(key);
  50. }
  51. }

  52. public void putAll(Map map) {
  53. synchronized (mutex) {
  54. m.putAll(map);
  55. }
  56. }

  57. public void clear() {
  58. synchronized (mutex) {
  59. m.clear();
  60. }
  61. }

  62. private transient Set keySet = null;
  63. private transient Set> entrySet = null;
  64. private transient Collection values = null;

  65. public Set keySet() {
  66. synchronized (mutex) {
  67. if (keySet == null)
  68. keySet = new SynchronizedSet(m.keySet(), mutex);
  69. return keySet;
  70. }
  71. }

  72. public Set> entrySet() {
  73. synchronized (mutex) {
  74. if (entrySet == null)
  75. entrySet = new SynchronizedSet>(
  76. (Set>) m.entrySet(), mutex);
  77. return entrySet;
  78. }
  79. }

  80. public Collection values() {
  81. synchronized (mutex) {
  82. if (values == null)
  83. values = new SynchronizedCollection(m.values(), mutex);
  84. return values;
  85. }
  86. }

  87. public boolean equals(Object o) {
  88. synchronized (mutex) {
  89. return m.equals(o);
  90. }
  91. }

  92. public int hashCode() {
  93. synchronized (mutex) {
  94. return m.hashCode();
  95. }
  96. }

  97. public String toString() {
  98. synchronized (mutex) {
  99. return m.toString();
  100. }
  101. }

  102. private void writeObject(ObjectOutputStream s) throws IOException {
  103. synchronized (mutex) {
  104. s.defaultWriteObject();
  105. }
  106. }
  107. }
2、Map map2= new ConcurrentHashMap();
ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现, concurrenthashmap是一个非常好的map实现,在高并发操作的场景下会有非常好的效率。实现的目的主要是为了避免同步操作时对整个map对象进行锁定从而提高并发访问能力 

实现原理 

锁分离 (Lock Stripping)

ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。

不变(Immutable)和易变(Volatile)

ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。HashEntry代表每个hash链中的一个节点,其结构如下所示:

Java代码  收藏代码
  1. static final class HashEntry {  
  2.     final K key;  
  3.     final int hash;  
  4.     volatile V value;  
  5.     final HashEntry next;  
  6. }  

可以看到除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改next引用值,所有的节点的修改只能从头部开始。对于put操作,可以一律添加到Hash链的头部。但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。这在讲解删除操作时还会详述。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。



refference:


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