您的位置 首页 java

「原创」Java并发编程系列28 | Copy-On-Write容器

「原创」Java并发编程系列28 | Copy-On-Write容器 「原创」Java并发编程系列28 | Copy-On-Write容器

2020年Java面试题库连载中

【000期】原创!2020年Java最全面试题库思维导图!

【001期】JavaSE面试题(一):面向对象

【002期】JavaSE面试题(二):基本数据类型与访问修饰符

【003期】JavaSE面试题(三):JavaSE语法(1)

【004期】JavaSE面试题(四):JavaSE语法(3)

【005期】JavaSE面试题(五):String类

【006期】JavaSE面试题(六):泛型

【007期】JavaSE面试题(七):异常

更多内容,点击上面蓝字查看

「原创」Java并发编程系列28 | Copy-On-Write容器

正文

「原创」Java并发编程系列28 | Copy-On-Write容器

前面两篇讲了并发编程中线程安全HashMap: ConcurrentHashMap ,那么作为同样使用频率很高的List和Set,J.U.C当然也提供了相应的线程安全集合,就是 Copy-On-Write 容器 CopyOnWriteArrayList CopyOnWriteArraySet

  1. COW设计思想

  2. 源码分析

  3. 应用场景

1. COW思想

这里的COW当然不是奶牛,而是 Copy-On-Write 的简称,即写时复制,是一种用于程序设计中的优化策略。

1.1 COW原理

COW的基本思路:

  • 当读取共享数据时,直接读取,不需要有其他操作(比如阻塞等待、复制等)。

  • 当写共享数据时,将旧数据复制出来一份作为新数据,只修改新数据,修改完新数据之后将新数据的引用赋值给原来数据的引用。在整个写数据的过程中,所有读取共享数据的操作都是读的旧数据。

1.2 COW优缺点分析

优点:

  1. 效率高。因为COW保证读和写操作的不是同一份数据,共享数据在读和写时都不需要阻塞其他来读取数据的线程,所以COW有很高的效率。

  2. 保证数据一致性。因为COW保证读和写操作的不是同一份数据,读数据的操作不会读到写了一半的数据,所以能够保证数据的最终一致性。

缺点:

  1. 数据实时性差。COW在写数据完成之前一直读取旧数据,而写数据又包括复制和修改的操作,花费时间较长,导致数据实时性较差。其实COW的设计思想就是通过牺牲数据的实时性来保证数据一致性的。

  2. 内存占用大。COW中有一步复制操作,将旧数据复制出来一份作为新数据,假如旧数据本身比较大,那么新数据也要占用同样大的内存空间。类似空间换时间的思想,这里用空间换数据一致性,当然也换取了读取数据的时间。

1.3 COW应用

COW的设计思想的一些应用:内存管理(如Linux的fork()函数),数据存储(如redis),文件管理系统(如Linux的文件管理系统),软件开发(如Java的Copy-On-Write容器)。

2. 源码分析

理解了 Copy-On-Write 思想, CopyOnWriteArrayList CopyOnWriteArraySet 的源码就很容易了。本文以 CopyOnWriteArrayList 源码为例来分析 Copy-On-Write 容器。

2.1 类结构

CopyOnWriteArrayList 只有两个属性,数组array用于存储数据,重入锁lock用于写操作的同步。

 public class CopyOnWriteArrayList<E> 
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
final transient ReentrantLock lock = new ReentrantLock;
private transient volatile Object array;
}

2.2 get

get方法获取数据,真的不用注释和讲解,最简单的代码。唯一需要注意的一点就是get方法是没有加锁的,不需要同步,读数据线程一定不会阻塞。

 public E get(int index) { 
return get(getArray, index);
}

final Object getArray {
return array;
}

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

2.3 add

代码很简单,基本过程就是按照COW思想的操作步骤:

  1. lock锁同步

  2. 旧数组复制出一个新数组

  3. 新数组添加元素

  4. 新数组引用赋给array

 public boolean add(E e) { 
final ReentrantLock lock = this.lock;
lock.lock;// 1. lock锁同步
try {
// 2. 旧数组复制出一个新数组
Object elements = getArray;
int len = elements.length;
Object newElements = Arrays.copyOf(elements, len + 1);
// 3. 新数组添加元素
newElements[len] = e;
// 4. 新数组引用赋给array
setArray(newElements);
return true;
} finally {
lock.unlock;// 解锁
}
}

3. 应用场景

Copy-On-Write并发容器用于读多写少的并发场,如商品的访问和更新,排行榜,白名单/黑名单等。

举例:一个充值排行榜的功能,排行榜会有很多人查看访问,但是只有充值之后才会修改排行榜上的数据,或者充值之后也不更新,只有每天晚上9点更新排行榜,标准的读多写少。

 public class CopyOnWriteArrayListTest { 
public static CopyOnWriteArrayList<Integer> rankIds = new CopyOnWriteArrayList<Integer>;
public static void addRankIds(int id) {
/*
* 获取id在rankIds中的排序,代码省略
* 假设id应该在排行榜中的第一个
*/
rankIds.add(0, id);
}
}

4. 总结

Copy-On-Write并发容器处理并发问题的原理:

  • 当读取共享数据时,直接读取,不需要有其他操作(比如阻塞等待、复制等)。

  • 当写共享数据时,将旧数据复制出来一份作为新数据,只修改新数据,修改完新数据之后将新数据的引用赋值给原来数据的引用。在整个写数据的过程中,所有读取共享数据的操作都是读的旧数据。

源码的并不只在于学习编程方法,更重要的是理解源码的设计思想,能够在开发和设计中运用。

并发系列文章汇总

【原创】01|开篇获奖感言

【原创】02|并发编程三大核心问题

【原创】03|重排序-可见性和有序性问题根源

【原创】04|Java 内存模型详解

【原创】05|深入理解 volatile

【原创】06|你不知道的 final

【原创】07|synchronized 原理

【原创】08|synchronized 锁优化

【原创】09|基础干货

【原创】10|线程状态

【原创】11|线程调度

【原创】12|揭秘 CAS

【原创】13|LockSupport

【原创】14|AQS 源码分析

【原创】15|重入锁 ReentrantLock

【原创】16|公平锁与非公平锁

【原创】17|读写锁八讲(上)

【原创】18|读写锁八讲(下)

【原创】19|JDK8新增锁StampedLock

【原创】20|StampedLock源码解析

【原创】21|Condition-Lock的等待通知

【原创】22|倒计时器CountDownLatch

【原创】22|倒计时器CountDownLatch

【原创】23|循环屏障CyclicBarrier

【原创】24|信号量Semaphore

【原创】25|交换器Exchangere

【原创】26|ConcurrentHashMap(上)

【原创】27|ConcurrentHashMap(下)

之前,给大家发过 四份 Java 面试宝典,这次新增了更全面的资料,相信在跳槽前准备准备,基本没大问题。

java 基础:设计模式等》 (初中级)

JVM :整理 BAT 最新题库》《并发编程》 (中高级)

《分布式微服务架构》《架构 | 软技能》 (资深)

《一线互联网公司面试指南》 (资深)

学习视频包含 深入运行时数据区、垃圾回收、详解类装载过程及类加载机制、手写 Spring-IOC 容器、 redis 入门到高性能缓存组件等等

 

获取方式:点“ ”, 在公众号后台打开,回复 666 】即可 领取,资料持续更新。

看到这里,证明有所收获

文章来源:智云一二三科技

文章标题:「原创」Java并发编程系列28 | Copy-On-Write容器

文章地址:https://www.zhihuclub.com/185799.shtml

关于作者: 智云科技

热门文章

发表回复

您的电子邮箱地址不会被公开。

网站地图