ArrayList是Java中最常用的集合类之一,但它不是线程安全的。在多线程环境下,如果多个线程同时操作一个ArrayList实例(例如添加、删除或修改元素),可能会导致数据不一致或抛出异常(如ConcurrentModificationException)
为什么ArrayList不是线程安全的?
ArrayList的内部实现是基于数组的动态扩容机制,其核心操作(如add、remove、set等)都没有使用同步机制(如synchronized)。因此,在多线程环境下,可能会出现以下问题:
1、数据不一致:多个线程同时修改ArrayList,可能导致数据丢失或覆盖
2、并发修改异常:在迭代过程中,如果另一个线程修改了ArrayList,可能会抛出ConcurrentModificationException。
如何线程安全地操作ArrayList?
1、使用Collections.synchronizedList
Collections.synchronizedList可以将ArrayList包装成一个线程安全的列表。它通过在方法级别加锁(synchronized)来保证线程安全
示例:
List<String> list = Collections.synchronizedList(new ArrayList<>());
这样,所有对列表的操作都会被同步,确保线程安全。但需要注意的是,迭代器(Iterator)仍然需要手动同步:
synchronized (list) { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); // 处理 item } }
2、使用CopyOnWriteArrayList
CopyOnWriteArrayList是java.util.concurrent包中的一个线程安全的列表实现。它通过在修改时创建底层数组的新副本来实现线程安全,适合多读写少的场景
List<String> list = new CopyOnWriteArrayList<>();
由于每次修改都会创建新副本,写操作的性能较差,但读操作不需要同步,性能较好
3、手动同步
如果你不想使用上述方法,可以在手动访问或修改ArrayList时使用同步块
List<String> list = new ArrayList<>(); // 添加元素 synchronized (list) { list.add("item"); } // 读取元素 synchronized (list) { String item = list.get(0); }
总结
-
ArrayList
本身不是线程安全的。 -
可以通过
Collections.synchronizedList
、CopyOnWriteArrayList
或手动同步来实现线程安全。 -
选择哪种方式取决于具体的应用场景和性能需求。