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!两者确实有相似之处,但也正是这点才是我们应该注意的地方,下面简单列出了两者的区别(具体功能的不同学习这篇文章后就会明白了): 数组 长 ...
随机推荐
- [转载] 散列表(Hash Table) 从理论到实用(下)
转载自: 白话算法(6) 散列表(Hash Table) 从理论到实用(下) [澈丹,我想要个钻戒.][小北,等等吧,等我再修行两年,你把我烧了,舍利子比钻戒值钱.] ——自扯自蛋 无论开发一个程序还 ...
- 使用PreTranslateMessage替代钩子函数处理键盘消息
2002年左右,我所在公司在开发基于H.323的VoIP电话系统(用了以色列一家公司的库,具体名字忘记了). 去电信科技研究院测试系统,同事发现处理键盘消息总有一些莫名其妙的问题,比如延迟或异常. 我 ...
- FreeBSD_11-系统管理——{Part_0-基础}
Tips: sysctl -d kern.maxvnodes #查看系统控制选项的含义 true > file #清空文件内容 alias ls 'ls -I(大写i)' #取消 root 的 ...
- 【洛谷P2296】寻找道路
反正图两边bfs #include<iostream> #include<cstdio> #include<queue> using namespace std; ...
- asp.net 文件批量移动重命名
最近闲时写了个批量移动重命名文件的工具 点击下载工具
- 12.我们不是在真空里谈软件工程, 软件要运行在硬件芯片上面, 下面看看一个计算机芯片的发展历史: http://perspectives.mvdirona.com/2014/09/august-21-2014-computer-history-museum-presentation/ http://mvdirona.com/jrh/TalksAndPapers/DileepBhandar
电脑芯片的诞生和发展是20世纪最伟大的发明之一,芯片技术决定了计算机升级换代的速度,决定了计算机小型化实现的程度,决定了计算机智能化的程度,决定了计算机普及化的应用深度. 1971年11月15日,英特 ...
- Windows 8.1 应用再出发 - 创建我的第一个应用
转眼间Windows 8.1已经发布了四个多月,之前因为开发需要对Windows 8.1新特性进行过零散的学习和使用,一直没有静下心来系统的学习过.近日部门有几名新同事加入,需要进行Windows 商 ...
- App技术框架
一.App技术框架的类型 图1 三种App技术框架之间的关系 目前App的技术框架基本分为三种(图1): (1)Native App:互动型,iOS.Android.WP各一套,而且要维护历史版本,要 ...
- Activity之间的通信
通常Activity之间的通信有三种方式:单向不传参数通信.单项传参数通信和双向通信. 这几种传递方式都需要通信使者Intent.以下将用代码来辅助理解. 1.单向不传递参数通信 public cla ...
- mfc 在VC的两个对话框类中传递参数的三种方法
弄了好久,今天终于把在VC中的对话框类之间传递参数的问题解决了,很开心,记录如下: 1. 我所建立的工程是一个基于MFC对话框的应用程序,一共有三个对话框,第一个对话框为主对话框,所对应的类为CTMD ...