您的位置:首页 > 健康 > 养生 > J.U.C Review - CopyOnWrite容器

J.U.C Review - CopyOnWrite容器

2024/10/6 0:36:03 来源:https://blog.csdn.net/yangshangwei/article/details/141829587  浏览:    关键词:J.U.C Review - CopyOnWrite容器

文章目录

  • 什么是CopyOnWrite容器
  • CopyOnWriteArrayList
    • 优点
    • 缺点
    • 源码示例
  • 仿写:CopyOnWriteMap的实现
  • 注意事项

在这里插入图片描述

什么是CopyOnWrite容器

CopyOnWrite容器是一种实现了写时复制(Copy-On-Write,COW)机制的并发容器。在并发场景中,多个线程可能同时访问同一资源,当某个线程需要修改数据时,系统会创建该数据的副本供其修改,而其他线程仍然可以访问原始数据。这种机制的主要优点是可以在读操作频繁的情况下,避免加锁,从而提高读取性能。

在Java中,从JDK 1.5开始,提供了两个主要的CopyOnWrite容器:CopyOnWriteArrayListCopyOnWriteArraySet。这两个容器的设计使得在“读多写少”的场景下,能够有效地提高并发性能。


CopyOnWriteArrayList

优点

  1. 无需同步CopyOnWriteArrayList在进行读取操作时不需要任何同步措施,极大地提高了读取性能。

  2. 避免异常:在遍历时,即使有其他线程对列表进行修改,也不会抛出ConcurrentModificationException异常,因为读取和写入操作分别作用于不同的容器。

缺点

  1. 内存开销:每次写操作都会复制整个容器,导致内存使用增加,可能引发频繁的垃圾回收(GC)问题。

  2. 数据一致性问题:由于写操作和读操作作用于不同的容器,读操作可能读取到旧数据,因此不能保证实时一致性。

源码示例

CopyOnWriteArrayListaddremove方法的实现逻辑如下:

public boolean add(E e) {// ReentrantLock加锁,保证线程安全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;// 将原容器引用指向新副本setArray(newElements);return true;} finally {// 解锁lock.unlock();}
}public E remove(int index) {// 加锁final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;E oldValue = get(elements, index);int numMoved = len - index - 1;if (numMoved == 0)// 如果要删除的是列表末端数据,拷贝前len-1个数据到新副本上,再切换引用setArray(Arrays.copyOf(elements, len - 1));else {// 否则,将要删除元素之外的其他元素拷贝到新副本中,并切换引用Object[] newElements = new Object[len - 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index + 1, newElements, index,numMoved);setArray(newElements);}return oldValue;} finally {// 解锁lock.unlock();}}

再来看看CopyOnWriteArrayList效率最高的读操作的源码

public E get(int index) {return get(getArray(), index);
}
 private E get(Object[] a, int index) {return (E) a[index];}

由上可见“读操作”是没有加锁,直接读取。


仿写:CopyOnWriteMap的实现

虽然Java并发包中没有提供CopyOnWriteMap,但可以参考CopyOnWriteArrayList实现一个简单的版本。以下是一个基本的实现示例:

import java.util.HashMap;
import java.util.Map;public class CopyOnWriteMap<K, V> implements Map<K, V> {private volatile Map<K, V> internalMap = new HashMap<>();public V put(K key, V value) {synchronized (this) {Map<K, V> newMap = new HashMap<>(internalMap);V oldValue = newMap.put(key, value);internalMap = newMap;return oldValue;}}public V get(Object key) {return internalMap.get(key);}public void putAll(Map<? extends K, ? extends V> m) {synchronized (this) {Map<K, V> newMap = new HashMap<>(internalMap);newMap.putAll(m);internalMap = newMap;}}
}

CopyOnWriteMap适用于“读多写少”的场景,例如一个搜索网站的黑名单管理系统。黑名单在特定时间更新,而在用户搜索时,系统需要快速检查关键字是否在黑名单中。


注意事项

使用CopyOnWrite容器时需要注意:

  1. 内存开销:应根据实际需求初始化容器的大小,以减少扩容带来的开销。

  2. 数据一致性CopyOnWrite容器只能保证最终一致性,不能保证实时一致性,因此不适用于需要立即读取写入数据的场景。

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com