ArrayList
什么是ArrayList就不赘述了,主要是在多线程的情况下,线程是不安全的,原因是一条线程在对集合进行遍历,然而另一条却对数组进行修改,那么这样就很容易触发ConcurrentModificationException
异常。
那么解决线程不安全的方法有:
-
用过时的方法Vactor,同ArrayList一样底层是一个数组,其中大部分方法都被synchronized关键字所修饰,扩容方法与ArrayList不同,是2倍的扩容。
-
用Collections工具类
List<String> list = Collections.synchronizedList(new ArrayList<>());
-
CopyOnWriteArrayList
List<String> list = CopyOnWriteArrayList<>();
效率高,没有同步锁。
CopyOnWrite
是并发的一种新思路,实现读写分离。在并发环境下保证了线程安全,如果线程进行写入操作,那么它会复制一个原本的集合,在新的集合内作添加或删除元素操作。待修改完成之后,再将原集合的引用指向新的集合。
-
好处是,可以高并发地对COW进行读和遍历操作,而不需要加锁,因为原集合不会添加任何元素。
-
坏处是,在某些时刻,内存占用可能会过大从而频繁触发GC,减低服务器性能。
-
使用COW需注意:
- 使用批量添加或删除方法,如addAll 或removeAll 操作,在高并发请求下,可以攒一下要添加或删除的元素,避免增加一个元素复制整个集合。
- 原因:假如几何数据是200MB,再写入10MB,那么某个时间段内占用的就达到(200MB * 2) + 10MB = 410MB,会频繁触发GC。
举例说明,CopyOnWriteArrayList 到底能有慢?(根据各自电脑不同,有所差异)
ArrayList:
List list = new ArrayList();
long a = System.currentTimeMillis();
for (int i = 0; i < 20 * 10000; i++) {
list.add(i);
}
long b = System.currentTimeMillis();
long c = b - a;
System.out.println(c); // 18
CopyOnWriteArrayList:
List cow = new CopyOnWriteArrayList();
long a = System.currentTimeMillis();
for (int i = 0; i < 20 * 10000; i++) {
cow.add(i);
}
long b = System.currentTimeMillis();
long c = b - a;
System.out.println(c); // 28016
愿世上没有Bug