线程安全的list之synchronizedList和CopyOnWriteArrayList
在上篇文章中我们已经介绍了其他的一些list集合,如ArrayList、linkedlist等。不清楚的可以看下上篇文章www.jianshu.com/p/6227ab5b3… 但是向ArrayList这些会出现线程不安全的问题,我们该怎样解决呢?接下来就是要介绍我们线程安全的list集合synchronizedList和CopyOnWriteArrayList。
一、synchronizedList
synchronizedList的使用方式:
public void test(){ArrayList<String> list = new ArrayList<>();List<String> sycList = Collections.synchronizedList(list);sycList.add("a");sycList.remove("a");}
复制代码
从上面的使用方式中我们可以看出,synchronizedList是将List集合作为参数来创建的synchronizedList集合。
synchronizedList为什么是线程安全的呢? 我们先来看一下他的源码:
@Overridepublic int size() {synchronized(mutex) {return backingList.size();}}@Overridepublic boolean isEmpty() {synchronized(mutex) {return backingList.isEmpty();}}@Overridepublic Object[] toArray() {synchronized(mutex) {return backingList.toArray();}}@Overridepublic boolean add(T e) {synchronized(mutex) {return backingList.add(e);}}@Overridepublic boolean remove(Object o) {synchronized(mutex) {return backingList.remove(o);}}
复制代码
我们大概贴了一些常用方法的源码,从上面的源码中我们可以看出,其实synchronizedList线程安全的原因是因为它几乎在每个方法中都使用了synchronized同步锁。
synchronizedList遍历为什么还需要加锁?
synchronizedList官方文档中给出的使用方式是以下方式:
List list = Collections.synchronizedList(new ArrayList());...synchronized (list) {Iterator i = list.iterator(); // Must be in synchronized blockwhile (i.hasNext())foo(i.next());}
复制代码
在以上源码中我们可以看出,官方文档是建议我们在遍历的时候加锁处理的。但是既然内部方法以及加了锁,为什么在遍历的时候还需要加锁呢?我们来看一下它的遍历方法:
@Overridepublic Iterator<T> iterator() {return backingList.iterator();}
复制代码
从以上源码可以看出,虽然内部方法中大部分都已经加了锁,但是iterator方法却没有加锁处理。那么如果我们在遍历的时候不加锁会导致什么问题呢? 试想我们在遍历的时候,不加锁的情况下,如果此时有其他线程对此集合进行add或者remove操作,那么这个时候就会导致数据丢失或者是脏数据的问题,所以如果我们对数据的要求较高,想要避免这方面问题的话,在遍历的时候也需要加锁进行处理。 但是既然是使用synchronized加锁进行处理的,那肯定避免不了一些锁开销。有没有效率更好的方式呢?那就是我们另一个主要的并发集合CopyOnWriteArrayList。
二、CopyOnWriteArrayList
CopyOnWriteArrayList是在执行修改操作时,copy一份新的数组进行相关的操作,在执行完修改操作后将原来集合指向新的集合来完成修改操作。具体源码如下:
/** The array, accessed only via getArray/setArray. */ private volatile transient Object[] array;//保证了线程的可见性 public void add(int index, E element) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;if (index > len || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);Object[] newElements;int numMoved = len - index;if (numMoved == 0)newElements = Arrays.copyOf(elements, len + 1); //copy一份比当前数组长度+1的array数组else {newElements = new Object[len + 1]; //将add的参数赋值System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index, newElements, index + 1,numMoved);}newElements[index] = element;setArray(newElements); //将原数组指向新的数组} finally {lock.unlock();}}public E remove(int index) {final ReentrantLock lock = this.lock;lock.lock();try {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)); //copy一份比当前数组长度-1的array数组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;} finally {lock.unlock();}}/*** 将原数组指向新的数组*/final void setArray(Object[] a) {array = a;}复制代码
从以上源码我们可以看出,它在执行add方法和remove方法的时候,分别创建了一个当前数组长度+1和-1的数组,将数据copy到新数组中,然后执行修改操作。修改完之后调用setArray方法来指向新的数组。在整个过程中是使用ReentrantLock可重入锁来保证不会有多个线程同时copy一个新的数组,从而造成的混乱。并且使用volatile修饰数组来保证修改后的可见性。读写操作互不影响,所以在整个过程中整个效率是非常高的。
总结
synchronizedList适合对数据要求较高的情况,但是因为读写全都加锁,所有效率较低。 CopyOnWriteArrayList效率较高,适合读多写少的场景,因为在读的时候读的是旧集合,所以它的实时性不高。
转载于:https://juejin.im/post/5d40105ae51d4561c273a649
线程安全的list之synchronizedList和CopyOnWriteArrayList相关推荐
- 如何线程安全地遍历List:Vector、CopyOnWriteArrayList
原文链接:http://blog.csdn.net/xiao__gui/article/details/51050793 遍历List的多种方式 在讲如何线程安全地遍历List之前,先看看通常我们遍历 ...
- java 多线程遍历list_如何线程安全地遍历List:Vector、CopyOnWriteArrayList
遍历List的多种方式 在讲如何线程安全地遍历List之前,先看看通常我们遍历一个List会采用哪些方式. 方式一: for(int i = 0; i < list.size(); i++) { ...
- 如何把class里的vector结构体memcpy出来_面试官:请说出线程安全的 ArrayList 有哪些,除了Vector...
以下环境是 JDK 1.8 ArrayList 的初始容量 面试官:你看过 ArrayList 的源码? Python 小星:看过 面试官:那你说下ArrayList 的初始容量是多少? Python ...
- arraylist 后往前遍历_面试官:请说出线程安全的 ArrayList 有哪些,除了Vector
以下环境是 JDK 1.8 ArrayList 的初始容量 面试官:你看过 ArrayList 的源码? Python 小星:看过 面试官:那你说下ArrayList 的初始容量是多少? Python ...
- JDK1.8源码分析:线程安全的CopyOnWriteArrayList与CopyOnWriteArraySet
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:服务端开发 blog.csdn.net/u01001 ...
- 全网最细 | 21张图带你领略集合的线程不安全
来源 | 悟空聊架构(ID:PassJava666) 本篇主要内容如下: 本篇主要内容 本篇所有示例代码已更新到 我的Github 本篇文章已收纳到我的Java在线文档 集合,准备团战 一.线程不安全 ...
- 超详细 | 21张图带你领略集合的线程不安全
来源 | 悟空聊架构 本篇主要内容如下: 本篇主要内容 本篇所有示例代码已更新到 我的Github 本篇文章已收纳到我的Java在线文档 线程不安全之ArrayList 集合框架有Map和Collec ...
- 「源码分析」CopyOnWriteArrayList 中的隐藏知识,你Get了吗?
前言 本觉 CopyOnWriteArrayList 过于简单,寻思看名字就能知道内部的实现逻辑,所以没有写这篇文章的想法,最近又仔细看了下 CopyOnWriteArrayList 的源码实现,大体 ...
- Java面试之ArrayList为什么线程不安全?
Collection线程不安全的举例 前言 1.当我们执行下面语句的时候,底层进行了什么操作 new ArrayList<Integer>(); 底层创建了一个空的数组,伴随着初始值为10 ...
- 集合的get方法中参数从多少开始_源码分析CopyOnWriteArrayList 中的隐藏知识,你Get了吗?...
欢迎点击 "未读代码" ,关注公众号,文章每周更新 杭州-阿里园区墙 前言 本觉 CopyOnWriteArrayList 过于简单,寻思看名字就能知道内部的实现逻辑,所以没有写这 ...
最新文章
- 【译】TCP Implementation in Linux
- boost::mp11::mp_rotate_right相关用法的测试程序
- 品牌管理-统一异常处理
- 软件设计师-不确定有限自动机到确定有限自动机转换的例子
- 【Koa】Error: Cannot find module ‘koa-router‘
- 【转】HEIF图片存储格式探秘
- 平安普惠系统上线申请表模板
- 信息安全方面优秀论文
- 腾讯公布员工数据:超 30 岁员工占近六成
- 漫画:什么是ConcurrentHashMap?
- android 最新 九宫格,Android开发中怎么显示一个九宫格图片
- Windows计算机操作系统基础知识点总结
- 关于数据库中FK的简单理解以及应用
- 士兵队列训练问题 (队列 c++)
- 剖析Vue实现双向数据绑定原理
- 东南大学计算机科学与技术夏令营,2019东南大学网络空间安全学院夏令营招生通知...
- NGS测序嵌合体是个需要去除的错误扩增序列
- Mac系统如何开启任何来源
- 三维目标检测算法汇总学习笔记
- 大连文思海辉php面试题,文思海辉前端面试题
热门文章
- 可视化大屏设计尺寸_大屏数据可视化设计规律
- String赋值方式
- C++ 编译运行报错 error: stray ‘\200’ in program 解决方案
- kubernetes视频教程笔记 (23)-存储-Volume
- 陕西师范大学计算机专业录取,陕西师范大学计算机类专业2016年在湖北理科高考录取最低分数线...
- python3语法学习第四天--字符串
- es6 将字符串转换为json_ES6中Json、String、Map、Object之间的转换
- vue3 Vite 2.0 Vue-Router 4.0 TypeScript elementPlus admin 须弥戒后台案例小结
- 后台json返回给ajax,Ajax 如何 得到后台返回 的json数据,正确的格式应该如何去写?...
- 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_11-webpack研究-npm和cnpm安装配置...