CopyOnWrite
1. 读多写少的场景引发的问题?
读写互斥,在写的过程中,大量的读操作阻塞,无法执行。
2. CopyOnWrite的思想来解决(参考JDK里的 CopyOnWriteArrayList 的源码)
原数据用于读操作,原数据的拷贝副本用于写操作,副本写完马上赋值给原数据,使用volatile关键字。
// 这个数组是核心的,因为用volatile修饰了 // 只要把最新的数组对他赋值,其他线程立马可以看到最新的数组 private transient volatile Object[] array; public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; // 对数组拷贝一个副本出来 Object[] newElements = Arrays.copyOf(elements, len + 1); // 对副本数组进行修改,在里面加入一个元素 newElements[len] = e; // 然后把副本数组赋值给volatile修饰的变量 setArray(newElements); return true; } finally { lock.unlock(); } } final void setArray(Object[] a) { array = a; }
3. 在kafka中的应用,内存缓冲用到的数据结构
private final ConcurrentMap<TopicPartition, Deque<RecordBatch>> batches = new CopyOnWriteMap<TopicPartition, Deque<RecordBatch>>();
// 典型的volatile修饰普通Map private volatile Map<K, V> map; @Override public synchronized V put(K k, V v) { // 更新的时候先创建副本,更新副本,然后对volatile变量赋值写回去 Map<K, V> copy = new HashMap<K, V>(this.map); V prev = copy.put(k, v); this.map = Collections.unmodifiableMap(copy); return prev; } @Override public V get(Object k) { // 读取的时候直接读volatile变量引用的map数据结构,无需锁 return map.get(k); }
转载出处