Collections.synchronizedList(new ArrayList())
来使ArrayList变成是线程安全的话,也是几乎都是每一个方法都加上synchronized关键字的,只不过它不是加在方法的声明处,而是方法的内部。多线程下for循环迭代Vector或者SynchronizedList,进行delete和get操做会发生数组下标错误的异常。java
for-each
(迭代器)来遍历咱们的集合,好处就是简洁、数组索引的边界值只计算一次。for-each
(迭代器)来作上面的操做,会抛出ConcurrentModificationException异常。若是想要完美解决上面所讲的问题,咱们能够在遍历前加锁:数组
- Hashtable、Vector加锁的粒度大(直接在方法声明处使用synchronized)
- ConcurrentHashMap、CopyOnWriteArrayList加锁粒度小(用各类的方式来实现线程安全,好比咱们知道的ConcurrentHashMap用了cas锁、volatile等方式来实现线程安全..)
- JUC下的线程安全容器在遍历的时候不会抛出ConcurrentModificationException异常
- CopyOnWriteArrayList是线程安全容器(相对于ArrayList),底层经过复制数组的方式来实现。
- CopyOnWriteArrayList在遍历的使用不会抛出ConcurrentModificationException异常,而且遍历的时候就不用额外加锁
- 元素能够为null
/** 可重入锁对象 */ final transient ReentrantLock lock = new ReentrantLock(); /** CopyOnWriteArrayList底层由数组实现,volatile修饰 */ private transient volatile Object[] array; final Object[] getArray() { return array; } final void setArray(Object[] a) { array = a; } // 初始化CopyOnWriteArrayList至关于初始化数组 public CopyOnWriteArrayList() { setArray(new Object[0]); }
CopyOnWriteArrayList底层就是数组,加锁就交由ReentrantLock来完成。安全
内存占用:若是CopyOnWriteArrayList常常要增删改里面的数据,常常要执行add()、set()、remove()
的话,那是比较耗费内存的。多线程
add()、set()、remove()
这些增删改操做都要复制一个数组出来。数据一致性:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。线程
setArray()
了)。可是线程A迭代出来的是原有的数据。