ArrayList 是 非线程安全的, CopyOnWriteArrayList 是一个线程安全,并且在读操作时无锁的ArrayList,且适合并发访问。对于集合元素数为10000,线程数量为100的情况下进行性能测试,随着元素数量和线程数量的增加,CopyOnWriteArrayList在增加元素和删除元素时的性能下降非常明显,并且性能会比ArrayList低。但在查找元素这点上随着线程数的增长,性能较ArrayList会好很多。
故在读多写少的并发场景中,CopyOnWriteArrayList较之ArrayList是更好的选择。
下面对两种方式的线程安全进行下测试:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ThreadSafeDemo {
public static int demo(final List list, final int testCount) throws InterruptedException {
ThreadGroup group = new ThreadGroup(list.getClass().getName() + "@" + list.hashCode());
final Random rand = new Random();
Runnable listAppender = new Runnable() { // 这里面实现了Runnable接口类,覆盖了它的run方法
public void run() {
try {
Thread.sleep(rand.nextInt(2));
} catch (InterruptedException e) {
return;
}
list.add("0");
}
};
for (int i = 0; i < testCount; i++) {
new Thread(group, listAppender, "InsertList-" + i).start();
//java.lang.
.Thread(
group,
target,
name)
}
while (group.activeCount() > 0) {
Thread.sleep(10);
}
return list.size();
}
public static void main(String[] args) throws InterruptedException {
List unsafeList = new ArrayList();
List safeList = Collections.synchronizedList(new ArrayList()); // 也可以换成new CopyToWriteArrayList
final int N = 10000;
for (int i = 0; i < 10; i++) {
unsafeList.clear();
safeList.clear();
int unsafeSize = demo(unsafeList, N);
int safeSize = demo(safeList, N);
System.out.println("unsafe/safe: " + unsafeSize + "/" + safeSize);
}
}
}
测试的结果为:
unsafe/safe: 9992/10000
unsafe/safe: 9996/10000
unsafe/safe: 9990/10000
unsafe/safe: 9992/10000
unsafe/safe: 9997/10000
unsafe/safe: 9997/10000
unsafe/safe: 9993/10000
unsafe/safe: 9995/10000
unsafe/safe: 9993/10000
unsafe/safe: 9995/10000
之所以会造成不安全线程list数不足10000,主要有两点:
首先我们来看ArrayList的源码:
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
主要问题出在size++这块,因为该方法不是线程安全的,所以就有可能出现以下两种情况:
1)线程A和线程B同样取得size=10,然后在相同位置10插入了两遍值,然后在都执行size++,结果size变成了12,这样下次再有线程进来时会在12的位置继续插入值,而11则变成了null,实验下果然如此,但是其实这并不能解释为什么list1.size的值会减少,只能解释为什么list中有的值为null
2) size++这个操作同样不是线程安全的,它分成两个步骤,第一,取size的位置,第二,size位置+1,这样就有可能A线程和B线程同时取到size的位置,然后+1,这样A,B线程执行完size++后,size的值为11而不是12,所以就会有不同的线程同时在一个位置赋值,导致list的数量不足。
分享到:
相关推荐
HashMap是线程不安全的,并允许null key 和 null value。 HashMap在我当前的jdk版本(11)的默认容量为0,在第一次添加元素的时候才初始化容量为 16, 之后才扩容为原来的2倍。 HashMap的扩容是根据 threshold决定的 : ...
在Java中,CopyOnWriteArrayList(写入时复制数组列表)是线程安全的集合类,它实现了List接口,并使用了"写入时复制"的策略来保证线程安全性。 CopyOnWriteArrayList的主要特点是:在进行修改操作(例如添加、修改...
线程安全和线程不安全的分别有哪些? Map接口有哪些实现类? 描述一下Map put的过程 如何得到一个线程安全的Map? HashMap有什么特点? ConcurrentHashMap是怎么分段分组的? ConcurrentHashMap是怎么分段分组的? ...
java biginteger 源码 MultiThreadMode Single Thread Execution模式 使用synchronized方法或代码块,只能保证某一段代码是只能由一个线程执行。...CopyOnWriteArrayList线程安全的类,适用于读操作频繁的场景。 Gua
CopyOnWriteArrayList是ArrayList的线程安全版本,从名字推测,CopyOnWriteArrayList是在有写操作的时候会copy一份数据,然后写完再设置成新的数据。CopyOnWriteArrayList适用于读多写少的并发场景。而...
高并发的情况下,一般都要求性能要给力,Vector 显然不够格,所以被遗忘在角落也是“罪有应得”啊。...那有些同学可能会说,可以使用 Collections.synchronizedList() 让 ArrayList 变成线程安全啊。
4.CopyOnWriteArrayList 因为线程安全的原因,致使性能降低很多,所以慎用; 5.Vector 没有传说中那么低的效率; 6.让Stack 来做List 的事可以,不过语义上Stack 不应该做过多的List 的事情; 7.在排序中,...
ArrayList 是我们常用的工具类之一,但是在多线程的情况下,ArrayList 作为共享变量时,并不是线程安全的。主要有以下两个原因: 1、ArrayList 自身的 elementData、size、modCount 在进行操作的时候,都没有加锁 ...
底层哈希表,基于hashCode的equals的比较方式,线程不安全,存取速度快。 SortedSet 标记: interface TreeSet 标记: class 实现comparable接口,元素是以二叉树的形式存放的。线程不安全 CopyOnWriteArraySet 标记:...
## 线程安全-并发容器JUC--原理以及分析 1.arrayList --copyonWriteArraylist 优缺点 2.HashSet,TreeSet -- CopyONWriteArraySet,ConcurrentSkipListSet 3.hashMap , treeMap -- ConcurrentHashMap,...
线程使用方式、两种互斥同步方法、线程协作、JUC、线程安全、内存模型、锁优化。 运行时数据区域、垃圾收集、类加载。 JavaIO知识,字符流,字节流,Socket,NIO。 操作系统 网络 面向对象 Gof 的 23 种设计模式。 ...
基于java tcp socket通信的拆包和装包源码 基础 集合 List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问,查找元素效率...1、Vector是线程同步的,所以它也是线程安全的,而ArrayLi
Collection体系集合1.1 List 表① ArrayList 类(数组)② Vector 类(数组、线程同步)③ LinkedList 类(链表)④ CopyOnWriteArrayList 类(线程安全且高效的List)1.2 Set 集合① HashSet 类(HashCode)② ...