/**
* 1、copyOnWriteArrayList 如何实现(non-fastfail)非快速失败机制???
* 2、Vector底层源码,主要看属性,构造函数、增删改查方法、明白ArrayList与Vector之间的区别与联系
* (底层数据结构、效率、扩容机制、是否线程安全)
*
*/
一、 ArrayList概述:
1、 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。
2、 ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
3、 ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输:该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
5、实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问:
E elementData(int index) {
return (E) elementData[index];
}
6、实现了Cloneable接口,能被克隆:
public Object clone() {
try {
ArrayList> v = (ArrayList>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
7、 ArrayList是线程非安全的;每个ArrayList在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。 注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。(涉及到快速失败机制)
8、fast-fail机制 ArrayList的自我保护机制
*********快速失败机制实际上依赖于modCout: 存储结构修改次数(增加和删除、扩容、缩容),和expectedModCount的对照
modCount定义于AbstractList,(ArrayList继承AbstractList)
使用迭代器或者foreach遍历时,如果调用集合add/remove方法修改集合的结构 ,则会抛出 ConcurrentModificationexception。
8.1、单线程,在iterator遍历的同时,插入新的参数。会导致快速失败
public class FastFailDemo {
public static void main(String[] args) {
Listlist = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for (Integer integer : list) {
list.add(integer);
System.out.println(integer);
}
}
}
8.2、单线程,使用randomaccess遍历List,即使在边读边写也不会出现快速失败
public class FastFailDemo {
public static void main(String[] args) {
Listlist = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for(int i =0 ; i< list.size(); i++) {
list.add(i);
System.out.println(i);
}
}
}
8.3、多线程,在一个线程读时,另一个线程写入list,读线程会快速失败
public class FastFailDemo {
public static void main(String[] args) {
Listlist = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
new MyThread1(list).start();
new MyThread2(list).start();
}
}
class MyThread1 extends Thread {
private Listlist;
public MyThread1( Listlist) {
this.list = list;
}
@Override
public void run() {
for (Integer integer : list) {
System.out.println("MyThread1" + list.size());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 extends Thread {
private Listlist;
public MyThread2( Listlist) {
this.list = list;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(i);
System.out.println("MyThread2 " + list.size());
}
}
}
二、arrayLsit的非快速失败机制和与vector底层实现的对比
1、非快速失败机制
1)、fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。若在多线程环境下使用fail-fast机制的集合,使用“java.util.concurrent包下的类”去取代“java.util包下的类”。
将代码:
List
替换为:
List
2)、
CopyOnWriteArrayList与ArrayList不同:
(01) 和ArrayList继承于AbstractList不同,CopyOnWriteArrayList没有继承于AbstractList,它仅仅只是实现了List接口。
(02) ArrayList的iterator()函数返回的Iterator是在AbstractList中实现的;而CopyOnWriteArrayList是自己实现Iterator。
(03) ArrayList的Iterator实现类中调用next()时,会“调用checkForComodification()比较‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator实现类中,没有所谓的checkForComodification(),更不会抛出ConcurrentModificationException异常!
虽然,获取到了更新后的ArrayList的大小;但是,当前迭代的结果并不是更新后的arrayList;
public class FastFailDemo {
public static void main(String[] args) {
Listlist = new CopyOnWriteArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(i);
}
new MyThread1(list).start();
new MyThread2(list).start();
}
}
class MyThread1 extends Thread {
private Listlist;
public MyThread1( Listlist) {
this.list = list;
}
@Override
public void run() {
for (Integer integer : list) {
System.out.println("MyThread1 大小:" + list.size() + " 当前值:" + integer);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 extends Thread {
private Listlist;
public MyThread2( Listlist) {
this.list = list;
}
@Override
public void run() {
for (int i = 5; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(i);
System.out.println("MyThread2 大小:" + list.size());
}
}
}
2、ArrayList:
1)、关键字解释:transient。 (序列化里用到)
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
public class UserInfo implements Serializable {
private static final long serialVersionUID = 996890129747019948L;
private String name;
private transient String psw;
public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
}
public String toString() {
return "name=" + name + ", psw=" + psw;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("张三", "123456");
System.out.println(userInfo);
try {
// 序列化,被设置为transient的属性没有被序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"UserInfo.out"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try {
// 重新读取内容
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"UserInfo.out"));
UserInfo readUserInfo = (UserInfo) in.readObject();
//读取后psw的内容为null
System.out.println(readUserInfo.toString());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
2)、构造方法:
ArrayList提供了三种方式的构造器,可以构造一个默认初始容量为10的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。
// ArrayList带容量大小的构造函数。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 新建一个数组
this.elementData = new Object[initialCapacity];
}
// ArrayList无参构造函数。默认容量是10。
public ArrayList() {
this(10);
}
// 创建一个包含collection的ArrayList
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
3)、 元素存储:
ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)这些添加元素的方法。
4)、元素直接读取
5)、元素删除E remove(int index); boolean remove(Object o); void fastRemove(int index);removeRange(int fromIndex,int toIndex)
2、对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。其实,Vector也基本上是对数组的操作;
1)、相似之处:
(1)、Vector和ArrayList的实现方式可以看出非常的类似(底层是数组),增删改查方法相似,同样有快速失败机制
(2)、扩充容量的方法ensureCapacityHelper。
与ArrayList相同,Vector在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就先看构造方法中传入的容量增长量参数CapacityIncrement是否为0,如果不为0,就设置新的容量为就容量加上容量增长量,如果为0,就设置新的容量为旧的容量的2倍,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量),而后同样用Arrays.copyof()方法将元素拷贝到新的数组。
(3)、同样在查找给定元素索引值等的方法中,两者都将该元素的值分为null和不为null两种情况处理,Vector中也允许元素为null。
2)、关于ArrayList和Vector区别如下:
(1)、ArrayList在内存不够时默认是扩展0.5倍,Vector是默认扩展1倍。
(2)、Vector提供indexOf(obj, start)接口,ArrayList没有。
(3)、Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销(很多方法加了synchronized同步语句,来保证线程安全)。