1 简述
CopyOnWriteArrayList 是从 JDK5 开始引进的并发集合之一,另一个是 CopyOnWriteArraySet,JDK 并没有提供 Map 的实现,我们之后将实现它。
2 COW
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。这种策略使得我们可以并发的读,而不用获取全局锁(但是写则需要获取锁)。体现的是读写分离的思想。但是因为每次添加都会复制原有的数组内容,因此存在内存开销问题。相对的,其他 Concurrent* 类的读写都会获取锁。
2.1 add 源码分析
1 // 使用 volatile 类型,只通过 getArray/setArray 访问 2 private transient volatile Object[] array; 3 4 @Override 5 public boolean add(E e) { 6 // 获取锁 7 final ReentrantLock lock = this.lock; 8 lock.lock(); 9 try { 10 11 Object[] elements = getArray(); 12 int len = elements.length; 13 // 复制原有的数组 14 Object[] newElements = Arrays.copyOf(elements, len + 1); 15 newElements[len] = e; 16 // 将原有数组指向新数组 17 setArray(newElements); 18 return true; 19 } finally { 20 lock.unlock(); 21 } 22 }
2.2 get 源码分析
1 public E get(int index) { 2 // 获取现数组,不需要加锁,所以可能会获取到旧的数据(当存在另一个线程对 List 进行写操作是) 3 return get(getArray(), index); 4 }
根据 2.1 ,这个方法获取到的数组可能会是旧的。
3 实现 CopyOnWriteMap
遵循上边的设计,我们可以围绕 COW 设计出 Map
1 package cn.pancc.purejdk.concurrent.fenxi; 2 3 import java.util.Collection; 4 import java.util.HashMap; 5 import java.util.Map; 6 import java.util.Set; 7 import java.util.concurrent.locks.ReentrantLock; 8 9 /** 10 * @author pancc 11 * @version 1.0 12 */ 13 public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable, java.io.Serializable { 14 private static final long serialVersionUID = -6907385757412808108L; 15 private volatile Map<K, V> map = new HashMap<>(); 16 17 public Map<K, V> getMap() { 18 return map; 19 } 20 21 public void setMap(Map<K, V> map) { 22 this.map = map; 23 } 24 25 private final ReentrantLock lock = new ReentrantLock(); 26 27 @Override 28 public int size() { 29 return getMap().size(); 30 } 31 32 @Override 33 public boolean isEmpty() { 34 return getMap().isEmpty(); 35 } 36 37 @Override 38 public boolean containsKey(Object key) { 39 return getMap().containsKey(key); 40 } 41 42 @Override 43 public boolean containsValue(Object value) { 44 return getMap().containsValue(value); 45 } 46 47 @Override 48 public V get(Object key) { 49 return getMap().get(key); 50 } 51 52 @Override 53 public V put(K key, V value) { 54 lock.lock(); 55 try { 56 Map<K, V> newMap = new HashMap<>(map); 57 V v = newMap.put(key, value); 58 this.map = newMap; 59 return v; 60 } finally { 61 lock.unlock(); 62 } 63 } 64 65 @Override 66 public V remove(Object key) { 67 lock.lock(); 68 try { 69 Map<K, V> newMap = new HashMap<>(map); 70 V v = newMap.remove(key); 71 this.map = newMap; 72 return v; 73 } finally { 74 lock.unlock(); 75 } 76 } 77 78 @Override 79 public void putAll(Map<? extends K, ? extends V> m) { 80 lock.lock(); 81 try { 82 Map<K, V> newMap = new HashMap<>(map); 83 newMap.putAll(m); 84 this.map = newMap; 85 } finally { 86 lock.unlock(); 87 } 88 } 89 90 @Override 91 public void clear() { 92 lock.lock(); 93 try { 94 this.map = new HashMap<>(); 95 } finally { 96 lock.unlock(); 97 } 98 } 99 100 @Override 101 public Set<K> keySet() { 102 return getMap().keySet(); 103 } 104 105 @Override 106 public Collection<V> values() { 107 return getMap().values(); 108 } 109 110 @Override 111 public Set<Entry<K, V>> entrySet() { 112 return getMap().entrySet(); 113 } 114 }
4 COW 的缺点
- 内存占用问题:每次写操作都会执行复制操作
- 数据一致性问题:写操作并不会及时影响到读操作获取的数据
5 应用场景
适用于读多写少的场景,如黑名单机制,单位信息缓存