ArrayList/Vector的原理、线程安全和迭代Fail-Fast
疑问
* ArrayList是非线程非安全的,具体是指什么?具体会产生什么问题?
* ArrayList的内部原理是什么?为什么可以动态扩容?
* Vector是线程安全的,具体是如何实现的?为什么不再推荐使用?还有它的适用场景吗?
* 迭代时集合发生了修改怎么办?什么是fail-fast?
线程安全和非线程安全
Vector内部是如何实现线程安全的?
public class Vector
{
Object[] elementData; // 存放元素的数组
int elementCount; // 存放元素的实际数量,默认的容量(capacity)是10
int capacityIncrement; // 当容量占满时,扩容量,如果未指定,则原先的2倍(doubled) // 构造函数
public Vector(int initialCapacity/* 初始容量 */,int capacityIncrement/*扩容量*/){}
}
其 capacity()/size()/isEmpty()/indexOf()/lastIndexOf()/removeElement()/addElement() 等方法均是 sychronized 的,所以,对Vector的操作均是线程安全的。
Vector是线程安全的,有问题吗?
如果用户知道自己是在单线程情况下运行,那么Vector本身的线程安全就没有必要了,耗费性能。JDK至少要提供一种非线程安全的List,供用户在不同的场景中选择,由此ArrayList出现了。ArrayList虽然是非线程安全的,但如果你想使用线程安全的ArrayList,可以在ArrayList的基础上,通过同步块来实现,或者使用同步包装器(Collections.synchronizedList),还可以使用J.U.C中的CopyOnWriteArrayList。但对于Vector,在其基础之上没有办法获得非线程安全的Vector(无法解耦)。这说明,在设计Vector时,没有做好分离性(数据结构功能和同步功能的分离)。
ArrayList的非线程安全会有什么问题?
Demo
final ArrayList<String> list = new ArrayList<String>(); // 多线程共享的ArrayList
for(int i=0;i<100;i++) // 多个线程同时进行写操作
{
new Thread(new Runnable(){
@Override
public void run() {
for(int j=0;j<1000;j++)
{
list.add("hello"); // 多线程下,此处引发ArrayIndexOutOfBoundsException
}
}}).start();
}
ArrayList的内部原理
public class ArrayList<E>
{
private Object[] elementData; // 存储元素的数组。其分配的空间长度是capacity。
private int size; // elementData存储了多少个元素。 public ArrayList(){this(10);}; // 默认capacity是10 boolean add(E e)
{
ensureCapacityInternal(size + 1); // capacity至少为 size+1
elementsData[size++]=e; // size++
return true;
}
void ensureCapacityInternal(int minCapacity){
if(minCapacity > elementData.length) // 扩容
grow(minCapacity);
}
void grow(int minCapacity){
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 约是原先的1.5倍。
elementData = Arrays.copyOf(elementData,newCapacity );
}
}
如何实现线程安全的ArrayList?
#1:自己手动同步
public static List<E> list = ... ;
lock.lock();
list.add();
lock.unlock();
#2:使用同步包装器
List<E> syncList = Collections.synchronizedList(new ArrayList<E>());
迭代时,需要包含在同步块当中
synchronized(syncList){
while(Iterator<E> iter = syncList.iterator();iter.hasNext();){}
}
#3:使用J.U.C中的CopyOnWriteArrayList。
迭代 / Fail-fast
什么是fail-fast?
一个fail-fast的系统是指当发现任何可能导致过程失败的情况时,立刻抛出错误。一个fail-fast的迭代器是指当迭代的集合正在迭代时检测到集合发生了修改,就立刻抛出一个异常。
ArrayList的fail-falst的实现
public class ArrayList
{
protected transient int modCount = 0; // 用来记录修改次数(继承自AbstractList) add() / remove() / trimToSize() / ensureCapacity() ...
{
modCount++; // 每次修改,modCount均自增
} class Itr implements Iterator<E>
{
int expectedModCount = modCount; // 记录modeCount当前值(一次快照) public E next() {
checkForComodification(); // next()操作之前,check一次
...
} public void remove(){
checkForComodification(); // remove()操作之前,check一次
...
ArrayList.this.remove(lastRet);
...
// 更新modCount的快照
// 这说明通过iter的Remove()来删除元素不会抛出ConcurrentModificationException
expectedModCount = modCount;
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
迭代时如何修改ArrayList?
Iterator<String> iter = list.iterator();
int j=0;
while(iter.hasNext())
{
System.out.println(iter.next());
if(j==3)
{
list.remove(0); // 出现 ConcurrentModificationException。
iter.remove(); // (单线程下)不会引发ConcurrentModificationException。但迭代器也只有这个修改相关的操作。
}
j++;
}
ConcurrentModificationException 这个异常看起来像是“多线程并发修改异常”,其实单线程下的迭代时修改也可能会出现这个异常。单线程下,迭代时通过集合自身的操作修改集合,会引发异常;通过迭代器修改(即 iter.remove() )不会引发 ConcurrentModificationException 。多线程下,迭代时通过迭代器修改可能会引发 ConcurrentModificationException ,此时应该使用线程安全的集合。
ArrayList/Vector的原理、线程安全和迭代Fail-Fast的更多相关文章
- Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
本文非常详尽地介绍了Java中的三个集合类 ArrayList,Vector与Stack <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整 ...
- ArrayList & Vector的源码实现
#ArrayList & Vector #####前言: 本来按照计划,ArrayList和Vector是分开讲的,但是当我阅读了ArrayList和Vector的源码以后,我就改变了注意,把 ...
- ArrayList Vector LinkedList 区别与用法
转载自: http://www.cnblogs.com/mgod/archive/2007/08/05/844011.html 最近用到了,所以依然是转载 ArrayList 和Vector是采用数组 ...
- Arraylist Vector Linkedlist区别和用法 (转)
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢 ...
- Java集合---ArrayList的实现原理
目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除 6) 调整数组容量 ...
- ava集合---ArrayList的实现原理
一.ArrayList概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存 ArrayList不是线程安全的,只能用在单线程环境下,多 ...
- Java集合:ArrayList的实现原理
Java集合---ArrayList的实现原理 目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除 ...
- ArrayList Vector LinkedList(一)
ArrayList Vector LinkedList 区别与用法 ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素, ...
- java集合(ArrayList,Vector,LinkedList,HashSet,TreeSet的功能详解)
说起集合,我们会潜意识里想到另外一个与之相近的名词——数组,OK!两者确实有相似之处,但也正是这点才是我们应该注意的地方,下面简单列出了两者的区别(具体功能的不同学习这篇文章后就会明白了): 数组 长 ...
随机推荐
- NSDate获取当前时间,并且转化为需要的格式
NSDate *date = [NSDate date]; NSDateFormatter *formatter = [[NSDateFormatter alloc]init]; [formatter ...
- Odoo10尝鲜:出勤登记
非常的cool,使用一个 联网的PAD 就能使用超级潮的 出勤登记功能 扫描 工牌条码,或者 从员工目录选择 员工, 例如 输入 PIN 密码, 防止"代打卡" ...
- SQlserver 行转列
列转行编程中很容易碰到,小弟在此总结下, 行转列暂时还没遇到,遇到再补充. 列转行: , , , , 以上都是以逗号分隔,分隔符可以自定义.
- window 2003 配置FTP +防火墙设置
2保险的做法是 不允许匿名登录,吧钩去掉 后面我们会添加一个用户,并且赋予权限 3 主目录 可以设置时当前计算机目录或者是另一台计算机目录(映射) FTP站点目录:浏览定位FTP文件所在站点,给予是否 ...
- ios 上架需要注意的问题
前段时间上架产品的时候,因为定位提示问题被拒绝了,而且集成了融云及时聊天的key,没有从测试环境升级到生产环境,所以记录一下APP上架所需要注意的事项. 1.程序没有崩溃性BUG 2.程序不存在逻辑性 ...
- 扩展欧几里德解的数量(51nod 1352)
题意:给出N,A,B:求A*x+ B*y = N+1 的大于0 的解的数量: 思路:先用exgcd求出大于0的初始解x,rest = N - x*A; sum = rest/LCM(A, B); ...
- 设计模式UML类图基础
1.聚合 聚合(aggregation)表示一种弱的"拥有"关系,体现的是A对象可以包含B对象,但是B对象不是A对象的一部分.如大雁是群居动物,每只大雁都属于一个雁群,一个雁群可以 ...
- Swing Note
2. Swing容器: 内容窗格.分层窗格.玻璃窗格和一个可选的菜单条.(这四个同时包含在根窗格里)(请分别向其中添加组件) ...
- qooxdoo 3.0 发布,JavaScript 的 GUI 框架
qooxdoo 3.0 是一个主要的版本,包含很多新特性和内部的改动,qooxdoo 3.0 是一个通用的 JS 框架,主要改进体现在 qx.Desktop, qx.Mobile, 和 qx.Webs ...
- MongoDB中的高级查询(二)
$mod取模运算 查询index对5取模运算等于1的数据. $not $not是元条件句,即可以用在任何其他条件之上.查询index对5取模运算不等于1的数据. $exists判断字段是否存在 查询出 ...