CopyOnWriteArrayList真的完全线程安全吗
我之前书上看到的说法是:Vector是相对线程安全,CopyOnWriteArrayList是绝对线程安全
这种说法其实有些问题,CopyOnWriteArrayList在某些场景下还是会报错的
CopyOnWriteArrayList解决了:1.多线程一边读一边写。2.多线程迭代时修改抛出并发修改异常问题
CopyOnWriteArrayList不能做到完全的线程安全参见下面的例子
CopyOnWriteArrayList是开发过程中常用的一种并发容器,多用于读多写少的并发场景。但是CopyOnWriteArrayList真的能做到完全的线程安全吗?
答案是并不能。
CopyOnWriteArrayList原理
我们可以看出当我们向容器添加或删除元素的时候,不直接往当前容器添加删除,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加删除元素,添加删除完元素之后,再将原容器的引用指向新的容器,整个过程加锁,保证了写的线程安全。
public boolean add(E e) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
}
}
public E remove(int index) {
synchronized (lock) {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
}
}
而因为写操作的时候不会对当前容器做任何处理,所以我们可以对容器进行并发的读,而不需要加锁,也就是读写分离。
public E get(int index) {
return get(getArray(), index);
}
一般来讲我们使用时,会用一个线程向容器中添加元素,一个线程来读取元素,而读取的操作往往更加频繁。写操作加锁保证了线程安全,读写分离保证了读操作的效率,简直完美。
数组越界
但想象一下如果这时候有第三个线程进行删除元素操作,读线程去读取容器中最后一个元素,读之前的时候容器大小为i,当去读的时候删除线程突然删除了一个元素,这个时候容器大小变为了i-1,读线程仍然去读取第i个元素,这时候就会发生数组越界。
测试一下,首先向CopyOnWriteArrayList里面塞10000个测试数据,启动两个线程,一个不断的删除元素,一个不断的读取容器中最后一个数据。
public void test(){
for(int i = 0; i<10000; i++){
list.add("string" + i);
}
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (list.size() > 0) {
String content = list.get(list.size() - 1);
}else {
break;
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if(list.size() <= 0){
break;
}
list.remove(0);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
运行,可以看出删除到第7个元素的时候就发生了数组越界

从上可以看出CopyOnWriteArrayList并不是完全意义上的线程安全,如果涉及到remove操作,一定要谨慎处理。
链接:https://www.jianshu.com/p/fc0ee3aaf2df
CopyOnWriteArrayList真的完全线程安全吗的更多相关文章
- EF Core使用SQL调用返回其他类型的查询 ASP.NET Core 2.0 使用NLog实现日志记录 CSS 3D transforms cSharp:use Activator.CreateInstance with an Interface? SqlHelper DBHelper C# Thread.Abort方法真的让线程停止了吗? 注意!你的Thread.Abort方法真
EF Core使用SQL调用返回其他类型的查询 假设你想要 SQL 本身编写,而不使用 LINQ. 需要运行 SQL 查询中返回实体对象之外的内容. 在 EF Core 中,执行该操作的另一种方法 ...
- 注意!你的Thread.Abort方法真的让线程停止了吗?
大家都知道在C#里面,我们可以使用 Thread.Start方法来启动一个线程,当我们想停止执行的线程时可以使用Thread.Abort方法来强制停止正在执行的线程,但是请注意,你确定调用了Threa ...
- C# Thread.Abort方法真的让线程停止了吗?
大家都知道在C#里面,我们可以使用 Thread.Start方法来启动一个线程,当我们想停止执行的线程时可以使用Thread.Abort方法来强制停止正在执行的线程,但是请注意,你确定调用了Threa ...
- CopyOnWriteArrayList线程安全分析
CopyOnWriteArrayList是开发过程中常用的一种并发容器,多用于读多写少的并发场景.但是CopyOnWriteArrayList真的能做到完全的线程安全吗? 答案是并不能. 一.Copy ...
- 线程安全的CopyOnWriteArrayList介绍
证明CopyOnWriteArrayList是线程安全的 先写一段代码证明CopyOnWriteArrayList确实是线程安全的. ReadThread.java import java.util. ...
- 线程安全的CopyOnWriteArrayList
证明CopyOnWriteArrayList是线程安全的 先写一段代码证明CopyOnWriteArrayList确实是线程安全的. ReadThread.java import java.util. ...
- 为什么线程安全的List推荐使用CopyOnWriteArrayList,而不是Vector
注:本系列文章中用到的jdk版本均为java8 相比很多同学在刚接触Java集合的时候,线程安全的List用的一定是Vector.但是现在用到的线程安全的List一般都会用CopyOnWriteArr ...
- 这道Java基础题真的有坑!我求求你,认真思考后再回答。
本文目录 一.题是什么题? 二.阿里Java开发规范. 2.1 正例代码. 2.2 反例代码. 三.层层揭秘,为什么发生异常了呢? 3.1 第一层:异常信息解读. 3.2 第二层:抛出异常的条件解读. ...
- Java - 安全的退出线程
stop() 存在的问题 使用 stop() 来退出线程是不安全的.它会解除由线程获取的所有锁,可能导致数据不一致. 举个例子: public class StopTest { public stat ...
随机推荐
- kernel笔记——进程调度
调度器完成以下任务: 时钟中断(或类似的定时器)时间内刷新进程的时间片,设置进程调度标志 系统调用返回或中断完成时检查调度标志 schedule函数 内核代码中完成进程调度的函数为schedule() ...
- web框架开发-分页器(Paginator)
Django有自带的分页器,可以将数据分在不同的页面中,并提供一些属性和方法实现对分页数据的操作.分页功能的类位于django/core/paginator.py中. 常用方法 # 分页器 # pag ...
- styled components草根中文版文档
1.styled components官网网址 https://www.styled-components.com/docs 以组件的形式来写样式. 1.1安装 yarn add styled-c ...
- python新式类与旧式类
python2.X是经典类[旧式类]: 是以深度优先[] 但是在Python2.x中,默认都是经典类,只有显式继承了object才是新式类,即:class Person(object):pass 新式 ...
- 【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- python第九章:面向对象--小白博客
面向对象介绍 一.面向对象和面向过程 面向过程:核心过程二字,过程即解决问题的步骤,就是先干什么后干什么 基于该思想写程序就好比在这是一条流水线,是一种机械式的思维方式 优点:复杂的过程流程化 缺点 ...
- [转帖]Oracle 12cR2使用经验
大规模升级来临,谈谈Oracle 12cR2使用经验 随着2019年2月13日,Oracle 19c (Oracle 12.2.0.3) for Exadata 版本发布,Oracle 12cR2体系 ...
- 7.docker日志收集
默认情况下,docker logs或者docker service logs显示命令的输出,就像在终端中以交互方式运行命令时一样.UNIX和Linux命令通常开在运行时间上三个I / O流,所谓的 S ...
- springboot连接数据库报错testWhileIdle is true, validationQuery not set
问题描述: 使用springboot连接数据库,启动的时候报错:testWhileIdle is true, validationQuery not set.但是不影响系统使用,数据库等一切访问正常. ...
- python doc格式转文本格式
首先python是不能直接读写doc格式的文件的,这是python先天的缺陷.但是可以利用python-docx (0.8.6)库可以读取.docx文件或.txt文件,且一路畅通无阻. 这样的话,可以 ...