简述
- 不可变对象的写操作往往都是使用 Copy-on-Write 方法解决的,当然 Copy-on-Write 的应用领域并不局限于 Immutability 模式。
Copy-on-Write 模式的应用领域
- CopyOnWriteArrayList 和 CopyOnWriteArraySet 这两个都是 Copy-on-Write 容器,读操作无锁,性能很强
- 弱一致性,中间读取的结果可能不是最新的,但是最终读取的结果是最新的。
- 更适用于写少读多的场景
- Copy-on-Write 在操作系统领域也有广泛的应用。Unix当中fork()子进程会复制父进程的整个地址空间;Linux在fork()时会让父子进程共享空间,只有写操作时才复制地址空间,从而使父子进程拥有各自的地址空间。
- 使用 Copy-on-Write 更多地体现的是一种延时策略,只有在真正需要复制的时候才复制,而不是提前复制好
- 在操作系统领域,除了创建进程用到了 Copy-on-Write,很多文件系统也同样用到了,例如 Btrfs (B-Tree File System)、aufs(advanced multi-layered unification filesystem)等。
- Docker 容器镜像的设计是 Copy-on-Write,涉及分布式源码管理系统 Git 背后的设计思想;Copy-on-Write 最大的应用领域还是在函数式编程领域。
- 函数式编程的基础是不可变性(Immutability),所以函数式编程里面所有的修改操作都需要 Copy-on-Write 来解决。
总结
- 目前 Copy-on-Write 在 Java 并发编程领域知名度不是很高,很多人都在无意中把它忽视了,但其实 Copy-on-Write 才是最简单的并发解决方案。它是如此简单,以至于 Java 中的基本数据类型 String、Integer、Long 等都是基于 Copy-on-Write 方案实现的。
- Copy-on-Write非常通用且应用广泛。不过,它也有缺点的,那就是消耗内存,每次修改都需要复制一个新的对象出来,好在随着自动垃圾回收(GC)算法的成熟以及硬件的发展,这种内存消耗已经渐渐可以接受了。
Java 提供了 CopyOnWriteArrayList,为什么没有提供 CopyOnWriteLinkedList 呢?
- 数组存储在连续内存,连续内存更有利于CPU加载和缓存,特点是增删慢,读取快;
- 链表数据结构存储在分散内存,特点是增删快,读取慢;
- 链表结构的设计初衷就是用于增删频繁,读取少的场景;
- CopyOnWrite使用场景:要求读取性能高,读取多,修改少,二者设计理念相违背,所以存在CopyOnWriteArrayList,而不存在CopyOnWriteLinkedList