并发线程同时添加到ArrayList – 会发生什么?

我们有多个线程调用ArrayList上的add(obj)

我的理论是,当两个线程同时调用add时,只有两个被添加的对象中的一个真的被添加到ArrayList 。 这是可信的吗?

如果是这样,你怎么解决这个问题? 使用像Vector这样的同步集合?

当ArrayList上的两个线程同时调用add时,会发生什么情况没有保证。 但是,我的经验是,两个对象都被罚款。 与列表相关的大多数线程安全问题在添加/删除时处理迭代。 尽pipe如此,我强烈build议不要使用具有multithreading和并发访问的vanilla ArrayList。

向量曾经是并发列表的标准,但现在标准是使用集合同步列表 。

另外,如果您将要花费任何时间使用Java中的线程,我强烈推荐Goetz等人的Java Concurrency in Practice。 本书更详细地介绍了这个问题。

任何事情都可能发生。 您可以正确添加两个对象。 你只能得到一个添加的对象。 你可能会得到一个ArrayIndexOutOfBoundsexception,因为底层数组的大小没有被正确调整。 或者可能发生其他事情。 只要说你不能依靠发生的任何行为就够了。

作为select,你可以使用Vector ,你可以使用Collections.synchronizedList ,你可以使用CopyOnWriteArrayList ,或者你可以使用一个单独的锁。 这一切都取决于你在做什么,以及你有什么样的控制访问收集。

你也可以得到一个null ,一个ArrayOutOfBoundsException ,或者执行的东西。 已经观察到HashMap在生产系统中进入无限循环。 你真的不需要知道什么可能出错,只是不要这样做。

你可以使用Vector ,但是它倾向于解决接口不够丰富的问题​​。 在大多数情况下,你可能会发现你想要一个不同的数据结构。

行为可能是未定义的,因为ArrayList不是线程安全的。 如果在迭代器正在进行迭代时修改列表,则会得到一个ConcurrentModificationExceptionexception。 您可以使用Collection.synchronizedList封装ArrayList,也可以使用线程安全的集合(有很多),或者只将add调用放入同步块中。

你可以使用List l = Collections.synchronizedList(new ArrayList()); 如果你想要线程安全版本的arrayList。

我想出了下面的代码来模拟一个真实世界的场景。

100个任务并行运行,他们将完成的状态更新到主程序中。 我使用CountDownLatch来等待任务完成。

 import java.util.concurrent.*; import java.util.*; public class Runner { // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>()) public List<Integer> completed = new ArrayList<Integer>(); /** * @param args */ public static void main(String[] args) { Runner r = new Runner(); ExecutorService exe = Executors.newFixedThreadPool(30); int tasks = 100; CountDownLatch latch = new CountDownLatch(tasks); for (int i = 0; i < tasks; i++) { exe.submit(r.new Task(i, latch)); } try { latch.await(); System.out.println("Summary:"); System.out.println("Number of tasks completed: " + r.completed.size()); } catch (InterruptedException e) { e.printStackTrace(); } exe.shutdown(); } class Task implements Runnable { private int id; private CountDownLatch latch; public Task(int id, CountDownLatch latch) { this.id = id; this.latch = latch; } public void run() { Random r = new Random(); try { Thread.sleep(r.nextInt(5000)); //Actual work of the task } catch (InterruptedException e) { e.printStackTrace(); } completed.add(id); latch.countDown(); } } } 

当我运行应用程序10次,至less3到4次程序没有打印正确的完成任务数量。 理想情况下,它应该打印100(如果没有例外发生)。 但在某些情况下,它打印98,99等

因此,它certificate了ArrayList的并发更新不会给出正确的结果。

如果我用同步版本replaceArrayList,程序输出正确的结果。

java.util.concurrent有一个线程安全的数组列表。 标准ArrayList不是线程安全的,并且multithreading同时更新时的行为是未定义的。 当一个或多个线程同时写入时,多个阅读器也可能会有奇怪的行为。

http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html

请注意,此实现不同步。 如果多个线程同时访问ArrayList实例,并且至less有一个线程在结构上修改了列表,则它必须在外部进行同步。

由于内部没有同步,所以你的理论是不合理的。

所以,事情变得不同步,带来不愉快和不可预测的结果。

你可以使用而不是ArrayList();

 Collections.synchronizedList( new ArrayList() ); 

要么

 new Vector(); 

我更喜欢synchronizedList ,因为它是:

  • 更快的50-100%
  • 可以使用已经存在的ArrayList的