CopyOnWriteArrayList线程安全分析
CopyOnWriteArrayList是开发过程中常用的一种并发容器,多用于读多写少的并发场景。但是CopyOnWriteArrayList真的能做到完全的线程安全吗? 答案是并不能。
一、CopyOnWriteArrayList原理
我们可以看出当我们向容器添加或删除元素的时候,不直接往当前容器添加删除,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加删除元素,添加删除完元素之后,再将原容器的引用指向新的容器,整个过程加锁,保证了写的线程安全。
1 public boolean add(E e) {
2 synchronized (lock) {
3 Object[] elements = getArray();
4 int len = elements.length;
5 Object[] newElements = Arrays.copyOf(elements, len + 1);
6 newElements[len] = e;
7 setArray(newElements);
8 return true;
9 }
10 }
11
12 public E remove(int index) {
13 synchronized (lock) {
14 Object[] elements = getArray();
15 int len = elements.length;
16 E oldValue = get(elements, index);
17 int numMoved = len - index - 1;
18 if (numMoved == 0)
19 setArray(Arrays.copyOf(elements, len - 1));
20 else {
21 Object[] newElements = new Object[len - 1];
22 System.arraycopy(elements, 0, newElements, 0, index);
23 System.arraycopy(elements, index + 1, newElements, index,
24 numMoved);
25 setArray(newElements);
26 }
27 return oldValue;
28 }
29 }
而因为写操作的时候不会对当前容器做任何处理,所以我们可以对容器进行并发的读,而不需要加锁,也就是读写分离。
1 public E get(int index) {
2 return get(getArray(), index);
3 }
一般来讲我们使用时,会用一个线程向容器中添加元素,一个线程来读取元素,而读取的操作往往更加频繁。写操作加锁保证了线程安全,读写分离保证了读操作的效率,简直完美。
二、数组越界
但想象一下如果这时候有第三个线程进行删除元素操作,读线程去读取容器中最后一个元素,读之前的时候容器大小为i,当去读的时候删除线程突然删除了一个元素,这个时候容器大小变为了i-1,读线程仍然去读取第i个元素,这时候就会发生数组越界。
测试一下,首先向CopyOnWriteArrayList里面塞10000个测试数据,启动两个线程,一个不断的删除元素,一个不断的读取容器中最后一个数据。
1 public void test(){
2 for(int i = 0; i<10000; i++){
3 list.add("string" + i);
4 }
5
6 new Thread(new Runnable() {
7 @Override
8 public void run() {
9 while (true) {
10 if (list.size() > 0) {
11 String content = list.get(list.size() - 1);
12 }else {
13 break;
14 }
15 }
16 }
17 }).start();
18
19 new Thread(new Runnable() {
20 @Override
21 public void run() {
22 while (true) {
23 if(list.size() <= 0){
24 break;
25 }
26 list.remove(0);
27 try {
28 Thread.sleep(10);
29 } catch (InterruptedException e) {
30 e.printStackTrace();
31 }
32 }
33 }
34 }).start();
35 }
运行,可以看出删除到第7个元素的时候就发生了数组越界:
从上可以看出CopyOnWriteArrayList并不是完全意义上的线程安全,如果涉及到remove操作,一定要谨慎处理。
CopyOnWriteArrayList线程安全分析的更多相关文章
- CopyOnWriteArrayList 源码分析 基于jdk1.8
CopyOnWriteArrayList 源码分析: 1:成员属性: final transient ReentrantLock lock = new ReentrantLock(); //内部是 ...
- Java线程问题分析定位
Java线程问题分析定位 分析步骤: 1.使用top命令查看系统资源占用情况,发现Java进程占用大量CPU资源,PID为11572: 2.显示进程详细列表命令:ps -mp 11572 -o THR ...
- Android系统--输入系统(七)Reader_Dispatcher线程启动分析
Android系统--输入系统(七)Reader_Dispatcher线程启动分析 1. Reader/Dispatcher的引入 对于输入系统来说,将会创建两个线程: Reader线程(读取事件) ...
- Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件
Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件 1. 输入按键 我们知道Android系统的按键分为三类:(1)Global Key;(2)Syste ...
- Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理
Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理 1. 回顾 我们知道Android输入系统是Reader线程通过驱动程序得到上报的输入事件,还要经过处理 ...
- Spring中获取request的几种方法,及其线程安全性分析
前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性. 原创不易,如果觉得文章对你有帮助,欢迎点赞.评论.文章有疏漏之处,欢迎批评指正. 欢迎 ...
- 【转】java线上程序排错经验2 - 线程堆栈分析
前言 在线上的程序中,我们可能经常会碰到程序卡死或者执行很慢的情况,这时候我们希望知道是代码哪里的问题,我们或许迫切希望得到代码运行到哪里了,是哪一步很慢,是否是进入了死循环,或者是否哪一段代码有问题 ...
- [No000016E]Spring 中获取 request 的几种方法,及其线程安全性分析
前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性. 原创不易,如果觉得文章对你有帮助,欢迎点赞.评论.文章有疏漏之处,欢迎批评指正. 欢迎 ...
- java线程dump分析工具
jstack和线程dump分析 java程序性能分析之thread dump和heap dump 一.[内存dump] jmap –dump:live,format=b,file=heap.bin ...
随机推荐
- 报表工具FastReport VCL 最新版发布!
新功能 为主要包类添加了类引用 在报表设计器中添加了SQL编辑器的自定义 为TfrxReport的操作添加了延迟的命令池:PrepareReport,ShowReport,LoadFrom.可以调用R ...
- SpringSecurity原理
一.认证的两种方式的介绍 1. 基于Session的认证方式 在之前的单体架构时代,我们认证成功之后都会将信息存入到Session中,然后响应给客户端的是对应的Session中数据的key,客户端会将 ...
- vue 异步提交php 两种方式传值
1.首先要在php的入口文件写上一条代码,允许异步提交 header("ACCESS-CONTROL-ALLOW-ORIGIN:*"); 2.在vue有两种方式将数据异步提交到ph ...
- Sqlite嵌入式数据库讲解
在计算机系统中,保存数据的方式一般有两种:1. 普通文件方式2. 数据库方式 相比于普通文件方式,使用数据库来管理大批量数据具有更高的效率与安全性. 数据库系统一般由3个部分构成1. 数据库2. 数据 ...
- Vue 全宇宙最浪 VSCode 配置、插件
别人的那一堆配置.插件我就不写了- 首先进入官网下载并安装. 基本配置 在编译器 文件 >> 首选项 >> 设置,可以拷贝相面的选项后搜索相关配置 设置制表符等于空格数为 2: ...
- ubuntu下vi不能正常使用的解决方法
现象 ubuntu中vi在编辑状态下方向键不能用,还有回格键不能删除等我们平时习惯的一些键都不能使用. 原因 ubuntu预装的是vim tiny版本,需要的是vim full版本. 方案 $sudo ...
- 用python you-get下载视频
安装python3后 安装you-get包: pip3 install you-get 下载视频: 打开windows终端:运行 you-get url 查看视频信息: you-get -i url ...
- jmeter_04_常用取样器
目录 常用取样器详解 http取样器 1.1 基本配置 1.2 高级配置 jdbc取样器 2.1 JDBC Connection Configuration 2.1.1 **Variable Name ...
- 【xenomai内核解析】系列文章大纲
xenomai内核解析 本博客为本人学习linux实时操作系统框架xenomai的一些记录,主要剖析xenomai内核实现,以及与linux相关的知识.方便读者定位具体文章,现列出本博客大纲,后续会陆 ...
- spring boot: 从配置文件中读取数据的常用方法(spring boot 2.3.4)
一,从配置文件中读取数据有哪些方法? 通常有3种用法: 1,直接使用value注解引用得到配置项的值 2, 封装到Component类中再调用 3, 用Environment类从代码中直接访问 生 ...