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!两者确实有相似之处,但也正是这点才是我们应该注意的地方,下面简单列出了两者的区别(具体功能的不同学习这篇文章后就会明白了): 数组 长 ...
随机推荐
- java容器简要概述
java中集合框架的概述 java集合类主要用于保存对象的. 常用的集合对象: Colletion接口,Collection接口是集合中的顶层容器,表示的是一组对象,它的下面有两个子接口List接口和 ...
- 通过indexPath找到对应的cell
在- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 这个方法中通过 ...
- anelife
无论如何要把安e生活做到最棒! 1.先用脑再用力!
- db2 怎么计算两个时间相差多少个月。如2015-10-10 和2014-1-12
SELECT timestampdiff (256, char(timestamp('2013-12-30 20:30:30') - timestamp('2001-09-26 15:24:23')) ...
- 巧用Javascript中的slice()
slice()是Javascript中Array的一个方法,定义是这样的. arrayObject.slice(start,end)作用是从原数组中从start到end位置截取出一个新的数组,返回值是 ...
- (Python )控制流语句if、for、while
这一节,我们将学习Python的控制流语句,主要包括if.for.while.break.continue 和pass语句 1. If语句 if语句也许是我们最熟悉的语句.其使用方法如下: x=inp ...
- LeetCode OJ-- Valid Number **@
https://oj.leetcode.com/problems/valid-number/ 判断给的串,是不是合理的 数字形式 主要问题在需求定义上吧 class Solution { public ...
- spring:bean的定义
一个完整的Bean的配置文件: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE beans ...
- Maven 跳过测试命令行参数 skip test
mvn package -Dmaven.test.skip=true
- Linux下查看所有CPU核心使用率的方法
两种方法: 1.方法一: sar -P ALL 查看历史CPU使用率: sar -f /var/log/sa/sa01 2.方法二: mpstat -P ALL