文章目录
1、从String类的不可变性实现说起
Immuability
模式:对象创建之后,状态就不再变化String
是不可变类,本质上是个字符数组
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/*省略其他方法和属性*/
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value;
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
// 返回的实际是一个新的 String对象(新的字符数组)
return new String(buf, true);
}
}
return this;
}
}
2、COW的应用领域
- OS:
- 类Unix OS下, 传统的
fork()
会创建父进程的一个完整副本,比如父进程地址空间用到1G内存,fork()时要复制父进程整个地址空间(1G)给子进程。耗时耗内存。 - Linux下,fork()时,不复制整个进程空间,而让父子进程共享同一个地址空间;只用在父进程或子进程要写入的时候才复制地址空间,使得父子进程有各自地址空间。
- 父子地址空间、数据总归要隔离,用COW体现的是延时策略,即“真正需要复制的时候才复制,而不是提前复制好”。
- 类Unix OS下, 传统的
- Java中COW
CopyOnWriteArrayList
CopyOnWriteArraySet
CopyOnWriteArrayList
支持无锁并发读,极大提升了读性能,但以内存复制为代价。
- FunctionProgramming
- 函数式编程基础 --> 不可变性,函数式编程中所有修改都基于 COW。
- Docker容器image
- Git 等
3、CopyOnWriteArrayList 与 CopyOnWriteArraySet的场景
- COW容器在修改时会复制整个数组,因此,若容器经常被修改或数组本身很大,不适用
- 适用于【读多写少、元素少、对读性能要求高、能容忍短暂脏读的场景】
4、一个案例
描述:一个RPC框架,Provder分实例部署,Consumer调用有一个LB的过程(见图)
RPC框架核心任务之一:维护服务路由关系,当Provider上线或者下线后,需要更新Consumer的路由表,简化后如图:
实现时的思考:
- 每次RPC调用都要调用LB算Provider 的IP+Port,LB需要通过路由表拿到所有接口路由。路由表的读性能很关键。
- 路由表对数据一致性要求不高。Provider从上线到反馈到路由表中,即使要 5s 也能接受。
- 路由表是典型【读多写少】
- 关键词: 【对读性能要求高,读多写少、弱一致性】、
CopyOnWriteArrayList
与CopyOnWriteArraySet
天生合适。。
再来看 Router 的设计:
- 思路1:更新Router的状态位来标识,则访问状态位的地方要同步访问,影响性能
- 思路2:采用Immuability 模式,每次上线、下线,创建、删除新的Router对象。
- 上下线频率低,思路2 较合适。
// 路由信息
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);
}
}
5、部分关键源码
并发容器中,实现List接口的只有 CopyOnWriteArrayList
。
原理简单说:
CopyOnWriteArrayList
内维护一个数组,成员变量 array --> 内部数组,所以读基于该 array 。迭代器 遍历的就是该array。
假设遍历 array 时,有一个写操作(加元素),CopyOnWriteArrayList
会:
- 1) 将 array复制一份
- 2)在新复制数组上增加元素
- 3)增加完元素后,将 array 指向该新数组
本质上,读写是并行的,遍历基于原来的array,写 基于新array。
要注意:
- 1)
CopyOnWriteArrayList
适用于写很少的场景,而且容忍读写短暂不一致。新加的元素可能不被很快遍历到 - 2)
CopyOnWriteArrayList
的迭代器只读(本质是个快照),不支持增删改。
CopyOnWriteArrayList
源码:
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
// ************删除无关代码****************
// *** get()源码
// Positional Access Operations
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
//**** add() 源码
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
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;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
}
/**
* 该API返回该list的快照
*/
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
/**
内部实现了 COWIterator 。
**/
static final class COWIterator<E> implements ListIterator<E> {
/** 全局变量 array 的快照 */
private final Object[] snapshot;
/** 游标 */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
@SuppressWarnings("unchecked")
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
/**
remove 时会抛出异常
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Set 时会抛出异常.
*/
public void set(E e) {
throw new UnsupportedOperationException();
}
/**
add()时会抛出异常
*/
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
Object[] elements = snapshot;
final int size = elements.length;
for (int i = cursor; i < size; i++) {
@SuppressWarnings("unchecked") E e = (E) elements[i];
action.accept(e);
}
cursor = size;
}
}