摘要:机制已经相关类和我们知道是用于替代的,是线程安全的容器。如果使用迭代器来做上面的操作,会抛出异常。实现原理是线程安全容器相对于,底层通过复制数组的方式来实现。线程在线程迭代的间隙中将部分的数据修改了已经调用了。
COW机制已经相关类
Vector和SynchronizedList
我们知道ArrayList是用于替代Vector的,Vector是线程安全的容器。因为它几乎在每个方法声明处都加了synchronized关键字来使容器安全。
如果使用Collections.synchronizedList(new ArrayList())来使ArrayList变成是线程安全的话,也是几乎都是每个方法都加上synchronized关键字的,只不过它不是加在方法的声明处,而是方法的内部。
多线程下for循环迭代Vector或者SynchronizedList,进行delete和get操作会发生数组下标错误的异常。
在JDK5以后,Java推荐使用for-each(迭代器)来遍历我们的集合,好处就是简洁、数组索引的边界值只计算一次。
如果使用for-each(迭代器)来做上面的操作,会抛出ConcurrentModificationException异常。
如果想要完美解决上面所讲的问题,我们可以在遍历前加锁:
遍历一下容器都要我加上锁,这这这不是要慢死了吗。的确是挺慢的。因为加锁粒度太大。
CopyOnWriteArrayList是同步List的替代品,CopyOnWriteArraySet是同步Set的替代品。
Hashtable、Vector加锁的粒度大(直接在方法声明处使用synchronized)
ConcurrentHashMap、CopyOnWriteArrayList加锁粒度小(用各种的方式来实现线程安全,比如我们知道的ConcurrentHashMap用了cas锁、volatile等方式来实现线程安全..)
JUC下的线程安全容器在遍历的时候不会抛出ConcurrentModificationException异常
所以一般来说,我们都会使用JUC包下给我们提供的线程安全容器,而不是使用老一代的线程安全容器。
CopyOnWriteArrayList实现原理
CopyOnWriteArrayList是线程安全容器(相对于ArrayList),底层通过复制数组的方式来实现。
CopyOnWriteArrayList在遍历的使用不会抛出ConcurrentModificationException异常,并且遍历的时候就不用额外加锁
元素可以为null
/** 可重入锁对象 */ final transient ReentrantLock lock = new ReentrantLock(); /** CopyOnWriteArrayList底层由数组实现,volatile修饰 */ private transient volatile Object[] array; final Object[] getArray() { return array; } final void setArray(Object[] a) { array = a; } // 初始化CopyOnWriteArrayList相当于初始化数组 public CopyOnWriteArrayList() { setArray(new Object[0]); }
CopyOnWriteArrayList底层就是数组,加锁就交由ReentrantLock来完成。
通过代码我们可以知道:在add(),set(),remove() 的时候就上锁,并复制一个新数组,增加操作在新数组上完成,将array指向到新数组中,最后解锁。
在修改时,复制出一个新数组,修改的操作在新数组中完成,最后将新数组交由array变量指向。
写加锁,读不加锁
CopyOnWriteArrayList缺点
内存占用:如果CopyOnWriteArrayList经常要增删改里面的数据,经常要执行add()、set()、remove()的话,那是比较耗费内存的。
因为我们知道每次add()、set()、remove()这些增删改操作都要复制一个数组出来。
数据一致性:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。
从上面的例子也可以看出来,比如线程A在迭代CopyOnWriteArrayList容器的数据。线程B在线程A迭代的间隙中将CopyOnWriteArrayList部分的数据修改了(已经调用setArray()了)。但是线程A迭代出来的是原有的数据。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/73516.html
摘要:体现的就是适配器模式。数组对象集合世界中的机制机制集合世界中比较常见的错误检测机制,防止在对集合进行遍历过程当中,出现意料之外的修改,会通过异常暴力的反应出来。而在增强循环中,集合遍历是通过进行的。 前言 学习情况记录 时间:week 2 SMART子目标 :Java 容器 记录在学习Java容器 知识点中,关于List的重点知识点。 知识点概览: 容器中的设计模式 从Array...
摘要:用于创建子进程等同于当前进程的副本。这个函数会有两次返回,将子进程的返回给父进程,返回给子进程。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。中断例程中,就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。 前言 只有光头才能变强 在读《Redis设计与实现》关于哈希表扩容的时候,发现这么一段话: 执行BGSAVE命令或者BGREWRITEAOF命令的...
摘要:可以看到,该结构体存储了关于变量值,有几个变量指向该结构体,变量类型,是否为引用变量等信息。这个就是写时复制,在作怪,他没有在赋值的时候就分裂成两个结构体,而是在我们改写其中一个变量时发生效果,属于一种慢复制也称慢分裂。 想要走到技术的天花板,那么学习过程中在于知其然且知其所以然。 今天我们来讨论一下PHP底层的写时复制(也称写时分裂)。 首先我们先来看看一段代码:showImg(ht...
摘要:问题农场一头小母牛,母牛每年生母牛。母牛岁产母牛,年后多少牛前提第一头母牛已经岁思路脉络面向对象基本思想,注意状态和行为,抽象出对应的属性和方法思想考虑类母牛农场一开始可能不会想到把农场设计为一个类。 问题: 农场一头小母牛, 母牛每年生母牛。 母牛5岁产母牛, 20年后多少牛? PS:前提:第一头母牛已经>=5岁 思路脉络: 面向对象基本思想,注意状态和行为,抽象出对...
摘要:快速了解继承在的继承关系里子类可以从获取父类的所有的公共和受保护成员字段方法和内部类。阻止继承有些情况下,我们可能不希望子类覆盖父类的方法,这时候,用关键字修饰方法即可实现该目的。 和现实世界中:子女可以继承父母的一些特征(如:基因)、财产等一样。OOP 中也有提供类似的特性,一个类完全可以从其它类里获得一些属性和方法,而不需要我们自己重新定义。这种特性简单但强大 (Simple an...
阅读 1665·2023-04-25 20:16
阅读 3803·2021-10-09 09:54
阅读 2641·2021-09-04 16:40
阅读 2458·2019-08-30 15:55
阅读 806·2019-08-29 12:37
阅读 2717·2019-08-26 13:55
阅读 2875·2019-08-26 11:42
阅读 3127·2019-08-23 18:26