解决线程处理共享数据除了之前的线程变量(ThreadLocal)和不可变(Immatable)模式外,还有一个方式就是CopyOnWrite模式(也叫COW ),但是有自己的适用场景。针对该模式 java juc提供了 CopyOnWriteArrayList、CopyOnWriteArraySet(底层使用CopyOnWriteArrayList保证去重实现)。
一、使用场景
1、读多写少
2、数据量比较小
3、对数据的一致性要求不是非常的高
二、原理
读数据:直接进行读取
写数据:将原数据进行Copy,再进行修改(所以需要一致性要求数据量比较小),最后将原对象的属性指向修改后的数组地址;
所以当并行修改和查询时,可能查询到旧数据(所以需要一致性要求不是特别高)
三、CopyOnWriteArrayList源码(其他方法与add类似)
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
private static final long serialVersionUID = 8673264195747942595L;
final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array;
private static final Unsafe UNSAFE;
private static final long lockOffset;
}
public boolean add(E var1) {
// 有修改时,先加锁(所以最好读多写少场景)
ReentrantLock var2 = this.lock;
var2.lock();
boolean var6;
try {
Object[] var3 = this.getArray();
int var4 = var3.length;
// 先Copy再修改
Object[] var5 = Arrays.copyOf(var3, var4 + 1);
var5[var4] = var1;
// 最后设置新值
this.setArray(var5);
var6 = true;
} finally {
var2.unlock();
}
return var6;
}
四、使用场景实例
比如微服务去定时去获取服务请求列表信息时,如:Ribbon等服务列表,就比较适用该场景。数据一致性要求不是很高,数据量比较小,读多写少。
demo数据结构如: ConcurrentHashMap<String, CopyOnWriteArraySet<Router>>
//路由信息
public final class Router{
private final String ip;
private final Integer port;
private final String iface;
//构造函数
public Router(String ip,
Integer port, String iface){
this.ip = ip;
this.port = port;
this.iface = iface;
}
//重写equals方法
public boolean equals(Object obj){
if (obj instanceof Router) {
Router r = (Router)obj;
return iface.equals(r.iface) &&
ip.equals(r.ip) &&
port.equals(r.port);
}
return false;
}
public int hashCode() {
//省略hashCode相关代码
}
}
//路由表信息
public class RouterTable {
//Key:接口名
//Value:路由集合
ConcurrentHashMap<String, CopyOnWriteArraySet<Router>>
rt = new ConcurrentHashMap<>();
//根据接口名获取路由表
public Set<Router> get(String iface){
return rt.get(iface);
}
//删除路由
public void remove(Router router) {
Set<Router> set=rt.get(router.iface);
if (set != null) {
set.remove(router);
}
}
//增加路由
public void add(Router router) {
Set<Router> set = rt.computeIfAbsent(
route.iface, r ->
new CopyOnWriteArraySet<>());
set.add(router);
}
}