ConcurrentHashMap与synchronizedMap源码解析

  一、讲解

  HashMap在并发执行put的时候会引发死循环,会因为多线程会导致HashMap的entry链表形成环形数据结构,一旦形成环形数据结构Entry的next永远不为空,就会产生死循环获取entry。

  HashTable容器使用synchronized来保证线程安全的,但是在线程竞争激烈的情况下,HashTable的效率非常低下,因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable的同步方法时会进入阻塞和轮询状态。如线程1使用put方法进行数据添加,线程2不仅不能使用put也不能使用get获取数据,因此竞争比较激烈。

  二、synchronizedMap

   Collections.synchronized*(m)将线程不安全集合变为线程安全集合,从源码来看由于synchronizedMap的作用就是将Map的各种方法添加了synchronized关键字进行修饰的。

  1    private static class SynchronizedMap<K,V>
  2         implements Map<K,V>, Serializable {
  3         private static final long serialVersionUID = 1978198479659022715L;
  4 
  5         private final Map<K,V> m;     // Backing Map
  6         final Object      mutex;        // Object on which to synchronize
  7 
  8         SynchronizedMap(Map<K,V> m) {
  9             this.m = Objects.requireNonNull(m);
 10             mutex = this;
 11         }
 12 
 13         SynchronizedMap(Map<K,V> m, Object mutex) {
 14             this.m = m;
 15             this.mutex = mutex;
 16         }
 17 
 18         public int size() {
 19             synchronized (mutex) {return m.size();}
 20         }
 21         public boolean isEmpty() {
 22             synchronized (mutex) {return m.isEmpty();}
 23         }
 24         public boolean containsKey(Object key) {
 25             synchronized (mutex) {return m.containsKey(key);}
 26         }
 27         public boolean containsValue(Object value) {
 28             synchronized (mutex) {return m.containsValue(value);}
 29         }
 30         public V get(Object key) {
 31             synchronized (mutex) {return m.get(key);}
 32         }
 33 
 34         public V put(K key, V value) {
 35             synchronized (mutex) {return m.put(key, value);}
 36         }
 37         public V remove(Object key) {
 38             synchronized (mutex) {return m.remove(key);}
 39         }
 40         public void putAll(Map<? extends K, ? extends V> map) {
 41             synchronized (mutex) {m.putAll(map);}
 42         }
 43         public void clear() {
 44             synchronized (mutex) {m.clear();}
 45         }
 46 
 47         private transient Set<K> keySet;
 48         private transient Set<Map.Entry<K,V>> entrySet;
 49         private transient Collection<V> values;
 50 
 51         public Set<K> keySet() {
 52             synchronized (mutex) {
 53                 if (keySet==null)
 54                     keySet = new SynchronizedSet<>(m.keySet(), mutex);
 55                 return keySet;
 56             }
 57         }
 58 
 59         public Set<Map.Entry<K,V>> entrySet() {
 60             synchronized (mutex) {
 61                 if (entrySet==null)
 62                     entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
 63                 return entrySet;
 64             }
 65         }
 66 
 67         public Collection<V> values() {
 68             synchronized (mutex) {
 69                 if (values==null)
 70                     values = new SynchronizedCollection<>(m.values(), mutex);
 71                 return values;
 72             }
 73         }
 74 
 75         public boolean equals(Object o) {
 76             if (this == o)
 77                 return true;
 78             synchronized (mutex) {return m.equals(o);}
 79         }
 80         public int hashCode() {
 81             synchronized (mutex) {return m.hashCode();}
 82         }
 83         public String toString() {
 84             synchronized (mutex) {return m.toString();}
 85         }
 86 
 87         // Override default methods in Map
 88         @Override
 89         public V getOrDefault(Object k, V defaultValue) {
 90             synchronized (mutex) {return m.getOrDefault(k, defaultValue);}
 91         }
 92         @Override
 93         public void forEach(BiConsumer<? super K, ? super V> action) {
 94             synchronized (mutex) {m.forEach(action);}
 95         }
 96         @Override
 97         public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
 98             synchronized (mutex) {m.replaceAll(function);}
 99         }
100         @Override
101         public V putIfAbsent(K key, V value) {
102             synchronized (mutex) {return m.putIfAbsent(key, value);}
103         }
104         @Override
105         public boolean remove(Object key, Object value) {
106             synchronized (mutex) {return m.remove(key, value);}
107         }
108         @Override
109         public boolean replace(K key, V oldValue, V newValue) {
110             synchronized (mutex) {return m.replace(key, oldValue, newValue);}
111         }
112         @Override
113         public V replace(K key, V value) {
114             synchronized (mutex) {return m.replace(key, value);}
115         }
116         @Override
117         public V computeIfAbsent(K key,
118                 Function<? super K, ? extends V> mappingFunction) {
119             synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);}
120         }
121         @Override
122         public V computeIfPresent(K key,
123                 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
124             synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);}
125         }
126         @Override
127         public V compute(K key,
128                 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
129             synchronized (mutex) {return m.compute(key, remappingFunction);}
130         }
131         @Override
132         public V merge(K key, V value,
133                 BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
134             synchronized (mutex) {return m.merge(key, value, remappingFunction);}
135         }
136 
137         private void writeObject(ObjectOutputStream s) throws IOException {
138             synchronized (mutex) {s.defaultWriteObject();}
139         }
140     }

  三、ConcurrentHashMap

  ConcurrentMap接口下有两个重要的实现:

  ConcurrentHashMap

  ConcurrentskipListMap(支持并发排序功能,弥补ConcurrentHashMap)

  ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成了16段(Segment也就是最高支持16个线程的并发修改操作,这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的时第一时间获取修改的内容,性能非常好)。

  锁分段技术

  ConcurrentHashMap是将Map分成很多小段的HashTable,由于HashTable容器在线程竞争激烈的情况下效率低下的原因,所有的线程都去竞争一把锁。ConcurrentHashMap就是使用锁分段技术将数据分成多段进行存储,然后给每一段数据配一把锁,当线程占用锁访问其中一段数据的时候,其他段的数据也能被其他线程访问,从而并发提高效率。

1     /**
2      * Creates a new, empty map with the default initial table size (16).
3      *容量是16
4      */
5     public ConcurrentHashMap() {
6     }
 1 /**
 2      * Maps the specified key to the specified value in this table.
 3      * Neither the key nor the value can be null.
 4      *
 5      * <p>The value can be retrieved by calling the {@code get} method
 6      * with a key that is equal to the original key.
 7      *
 8      * @param key key with which the specified value is to be associated
 9      * @param value value to be associated with the specified key
10      * @return the previous value associated with {@code key}, or
11      *         {@code null} if there was no mapping for {@code key}
12      * @throws NullPointerException if the specified key or value is null
13      */
14     public V put(K key, V value) {
15         return putVal(key, value, false);
16     }
17 
18     /** Implementation for put and putIfAbsent */
19     final V putVal(K key, V value, boolean onlyIfAbsent) {
20         if (key == null || value == null) throw new NullPointerException();
21         int hash = spread(key.hashCode());
22         int binCount = 0;
23         for (Node<K,V>[] tab = table;;) {
24             Node<K,V> f; int n, i, fh;
25             if (tab == null || (n = tab.length) == 0)
26                 tab = initTable();
27             else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
28                 if (casTabAt(tab, i, null,
29                              new Node<K,V>(hash, key, value, null)))
30                     break;                   // no lock when adding to empty bin
31             }
32             else if ((fh = f.hash) == MOVED)
33                 tab = helpTransfer(tab, f);
34             else {
35                 V oldVal = null;
36                 synchronized (f) {
37                     if (tabAt(tab, i) == f) {
38                         if (fh >= 0) {
39                             binCount = 1;
40                             for (Node<K,V> e = f;; ++binCount) {
41                                 K ek;
42                                 if (e.hash == hash &&
43                                     ((ek = e.key) == key ||
44                                      (ek != null && key.equals(ek)))) {
45                                     oldVal = e.val;
46                                     if (!onlyIfAbsent)
47                                         e.val = value;
48                                     break;
49                                 }
50                                 Node<K,V> pred = e;
51                                 if ((e = e.next) == null) {
52                                     pred.next = new Node<K,V>(hash, key,
53                                                               value, null);
54                                     break;
55                                 }
56                             }
57                         }
58                         else if (f instanceof TreeBin) {
59                             Node<K,V> p;
60                             binCount = 2;
61                             if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
62                                                            value)) != null) {
63                                 oldVal = p.val;
64                                 if (!onlyIfAbsent)
65                                     p.val = value;
66                             }
67                         }
68                     }
69                 }
70                 if (binCount != 0) {
71                     if (binCount >= TREEIFY_THRESHOLD)
72                         treeifyBin(tab, i);
73                     if (oldVal != null)
74                         return oldVal;
75                     break;
76                 }
77             }
78         }
79         addCount(1L, binCount);
80         return null;
81     }
 1 /**
 2      * Returns the value to which the specified key is mapped,
 3      * or {@code null} if this map contains no mapping for the key.
 4      *
 5      * <p>More formally, if this map contains a mapping from a key
 6      * {@code k} to a value {@code v} such that {@code key.equals(k)},
 7      * then this method returns {@code v}; otherwise it returns
 8      * {@code null}.  (There can be at most one such mapping.)
 9      *
10      * @throws NullPointerException if the specified key is null
11      */
12     public V get(Object key) {
13         Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
14         int h = spread(key.hashCode());
15         if ((tab = table) != null && (n = tab.length) > 0 &&
16             (e = tabAt(tab, (n - 1) & h)) != null) {
17             if ((eh = e.hash) == h) {
18                 if ((ek = e.key) == key || (ek != null && key.equals(ek)))
19                     return e.val;
20             }
21             else if (eh < 0)
22                 return (p = e.find(h, key)) != null ? p.val : null;
23             while ((e = e.next) != null) {
24                 if (e.hash == h &&
25                     ((ek = e.key) == key || (ek != null && key.equals(ek))))
26                     return e.val;
27             }
28         }
29         return null;
30     }

 

posted @ 2019-09-21 11:22  houstao  阅读(168)  评论(0编辑  收藏  举报