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的数量不足。
分享到:
相关推荐
在Java编程中,集合框架是数据管理的核心部分。...此外,Java并发库(如`ConcurrentHashMap`、`CopyOnWriteArrayList`等)提供了更高效且线程安全的解决方案,可以在性能和线程安全之间找到更好的平衡。
然而,ArrayList并不是线程安全的,这意味着在多线程环境下,如果多个线程同时对ArrayList进行操作,特别是结构上的修改,可能会导致数据不一致或者抛出异常。线程不安全主要体现在以下几个方面: 1. **无同步处理...
ArrayList源码和多线程安全问题分析 在 Java 编程语言中,ArrayList 是一个常用的集合类,它提供了动态数组的实现,能够存储大量的数据。但是,在多线程环境下,ArrayList 并不是线程安全的。这篇文章主要介绍了 ...
这些集合确保了在多个线程访问时的数据一致性、完整性和安全性,避免了竞态条件、死锁和其他并发问题。Java提供了一系列的线程安全集合类,它们是专门为多线程环境设计的。 首先,我们要了解什么是线程安全。线程...
如果需要线程安全的动态数组,可以选择Vector,但应考虑其性能影响,也可以考虑使用`Collections.synchronizedList(List<T> list)`将ArrayList转换为线程安全的列表,或者使用`CopyOnWriteArrayList`,它在读多写少...
线程安全的集合对象,如Vector、HashTable和StringBuffer,相比于非线程安全的ArrayList、LinkedList、HashMap、HashSet、TreeMap和TreeSet、StringBuilder等,提供了额外的线程安全保障,但可能会牺牲一定的性能。...
在实际应用中,选择`ArrayList`还是其他集合类型,如`LinkedList`或`HashSet`,应根据具体需求来决定,例如是否需要保持元素顺序、是否频繁进行插入和删除、是否需要线程安全等。 总结来说,`ArrayList`是Java集合...
虽然 `Vector` 在某些情况下可以满足需求,但由于其性能问题和潜在的线程安全陷阱,现代Java编程实践中更倾向于使用其他集合类,如 `ArrayList` 或并发集合类。理解不同集合类的特点及其适用场景对于编写高效、可靠...
此外,对于大数据量的情况,考虑使用并发集合,如ConcurrentLinkedQueue或CopyOnWriteArrayList,以提高多线程环境下的性能。 总的来说,理解ArrayList和LinkedList的基本特性和应用场景,以及如何处理与之相关的...
- **并发容器**:如ArrayList、HashMap的并发版本(CopyOnWriteArrayList、ConcurrentHashMap),设计为线程安全,但效率更高。 - **阻塞队列**:如ArrayBlockingQueue、LinkedBlockingQueue,提供生产者-消费者...
2. **线程安全**:ArrayList和LinkedList不是线程安全的,如果在多线程环境中使用,需要手动添加同步机制,或者选择Vector。 3. **内存消耗**:LinkedList比ArrayList和Vector占用更多的内存,因为它需要存储额外的...
Java并发容器CopyOnWriteArrayList是Java并发包中提供的一个并发容器,实现了线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现。这种读写分离的并发策略使得CopyOnWriteArrayList容器允许...
在Java中,CopyOnWriteArrayList(写入时复制数组列表)是线程安全的集合类,它实现了List接口,并使用了"写入时复制"的策略来保证线程安全性。 CopyOnWriteArrayList的主要特点是:在进行修改操作(例如添加、修改...
同时,关注集合框架的并发支持,如ConcurrentHashMap和CopyOnWriteArrayList等,它们在多线程环境下的性能优化至关重要。 最后,了解集合框架的发展历程,比如从Java 1.0到现在的改进,可以帮助你更好地理解设计...
如果需要线程安全,可以考虑使用`Collections.synchronizedList()`将ArrayList转换为线程安全的列表,或者使用`CopyOnWriteArrayList`类。 ArrayList的容量会随着元素数量的增加自动扩展。默认情况下,当容量达到...
- **效率**:由于没有线程安全的开销,ArrayList在单线程环境中的效率更高,特别适合于频繁进行随机访问和尾部插入、删除操作的场景。 3. **LinkedList** - **数据结构**:LinkedList是基于双向链表实现的,每个...
对于并发场景,可以使用CopyOnWriteArrayList(线程安全的ArrayList变体)和ConcurrentHashMap(线程安全的HashMap变体)。 总之,ArrayList和HashMap是Java集合框架中的重要组件,理解它们的工作原理和适用场景,...
如果需要线程安全,可以考虑使用CopyOnWriteArrayList。 2. **随机访问**:由于ArrayList基于数组,所以可以通过索引来快速访问任何位置的元素,随机访问性能优秀。 3. **增删操作效率**:在ArrayList的末尾添加或...
在设计和使用 ArrayList 时,需要考虑线程安全问题,并根据实际需求选择合适的构造方法和操作方式。在面试中,深入理解 ArrayList 的源码和其与其他数据结构的区别是展示 Java 基础技能的重要方面。
在多线程环境下,要么使用`synchronized`方法,要么使用`CopyOnWriteArrayList`(线程安全但不支持修改操作)。 - 使用线程安全的ArrayList可能会降低性能,因为每次操作都需要进行同步。 总的来说,ArrayList是...