Java 并发线程同时添加到 ArrayList - 会发生什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2715983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Concurrent threads adding to ArrayList at same time - what happens?
提问by Marcus Leon
We have multiple threads calling add(obj)
on an ArrayList
.
我们有多个线程调用add(obj)
上ArrayList
。
My theory is that when add
is called concurrently by two threads, that only one of the two objects being added is really added to the ArrayList
. Is this plausable?
我的理论是,当add
被两个线程同时调用时,被添加的两个对象中只有一个被真正添加到ArrayList
. 这有道理吗?
If so, how do you get around this? Use a synchronized collection like Vector
?
如果是这样,你如何解决这个问题?使用像Vector
?
采纳答案by derivation
There is no guaranteed behavior for what happens when add is called concurrently by two threads on ArrayList. However, it has been my experience that both objects have been added fine. Most of the thread safety issues related to lists deal with iteration while adding/removing. Despite this, I strongly recommend against using vanilla ArrayList with multiple threads and concurrent access.
当 ArrayList 上的两个线程同时调用 add 时会发生什么,没有保证的行为。但是,根据我的经验,这两个对象都添加得很好。大多数与列表相关的线程安全问题在添加/删除时处理迭代。尽管如此,我强烈建议不要使用具有多线程和并发访问的 vanilla ArrayList。
Vector used to be the standard for concurrent lists, but now the standard is to use the Collections synchronized list.
Vector 曾经是并发列表的标准,但现在标准是使用集合同步列表。
Also I highly recommend Java Concurrency in Practice by Goetz et al if you're going to be spending any time working with threads in Java. The book covers this issue in much better detail.
另外,如果您打算花时间在 Java 中处理线程,我强烈推荐 Goetz 等人的 Java Concurrency in Practice。这本书更详细地讨论了这个问题。
回答by Shamik
you can use List l = Collections.synchronizedList(new ArrayList());
if you want thread safe version of arrayList.
List l = Collections.synchronizedList(new ArrayList());
如果你想要 arrayList 的线程安全版本,你可以使用。
回答by WhirlWind
http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html
http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally.
请注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。
Since there is no synchronization internally, what you theorize is not plausible.
由于内部没有同步,你的理论是不合理的。
So, things get out of sync, with unpleasant and unpredictable results.
因此,事情变得不同步,产生令人不快和不可预测的结果。
回答by Tom Cabanski
java.util.concurrent has a thread-safe array list. The standard ArrayList is not thread-safe and the behavior when multiple threads update at the same time is undefined. There can also be odd behaviors with multiple readers when one or more threads is writing at the same time.
java.util.concurrent 有一个线程安全的数组列表。标准 ArrayList 不是线程安全的,多个线程同时更新时的行为是未定义的。当一个或多个线程同时写入时,多个读取器也会出现奇怪的行为。
回答by Tom Hawtin - tackline
You could also get a null
, an ArrayOutOfBoundsException
, or something left up to the implementation. HashMap
s have been observed to go into an infinite loop in production systems. You don't really need to know what might go wrong, just don't do it.
您还可以获得 a null
、 anArrayOutOfBoundsException
或其他由实现决定的东西。HashMap
已经观察到在生产系统中进入无限循环。你真的不需要知道什么可能会出错,只是不要这样做。
You could use Vector
, but it tends to work out the interface is not rich enough. You will probably find that you want a different data structure in most cases.
您可以使用Vector
,但它往往会发现界面不够丰富。您可能会发现在大多数情况下您需要不同的数据结构。
回答by Robby Pond
The behavior is probably undefined since ArrayList isn't threadsafe. If you modify the list while an Iterator is interating over it then you will get a ConcurrentModificationException. You can wrap the ArrayList with Collection.synchronizedList or use a thread-safe collection (there are many), or just put the add calls in a synchronized block.
该行为可能未定义,因为 ArrayList 不是线程安全的。如果在迭代器对其进行交互时修改列表,则会得到 ConcurrentModificationException。您可以使用 Collection.synchronizedList 包装 ArrayList 或使用线程安全集合(有很多),或者只是将 add 调用放在同步块中。
回答by Matthew T. Staebler
Any number of things could happen. You could get both objects added correctly. You could get only one of the objects added. You could get an ArrayIndexOutOfBounds exception because the size of the underlying array was not adjusted properly. Or other things may happen. Suffice it to say that you cannot rely on any behavior occurring.
任何数量的事情都可能发生。您可以正确添加两个对象。您只能添加一个对象。您可能会收到 ArrayIndexOutOfBounds 异常,因为底层数组的大小未正确调整。否则可能会发生其他事情。可以说你不能依赖任何发生的行为。
As alternatives, you could use Vector
, you could use Collections.synchronizedList
, you could use CopyOnWriteArrayList
, or you could use a separate lock. It all depends on what else you are doing and what kind of control you have over access to the collection.
作为替代方案,您可以使用Vector
、您可以使用Collections.synchronizedList
、您可以使用CopyOnWriteArrayList
,或者您可以使用单独的锁。这完全取决于您正在做什么以及您对集合的访问权限的类型。
回答by Vishal John
I came up with the following code to mimic somewhat a real world scenario.
我想出了以下代码来模拟真实世界的场景。
100 tasks are run in parallel and they update their completed status to the main program. I use a CountDownLatch to wait for task completion.
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();
}
}
}
When i ran the application 10 times and at least 3 to 4 times the program did not print correct number of completed tasks. Ideally it should print 100(if no exceptions happen). But in some cases it was printing 98, 99 etc.
当我运行应用程序 10 次并且至少运行 3 到 4 次时,程序没有打印正确数量的已完成任务。理想情况下它应该打印 100(如果没有异常发生)。但在某些情况下,它会打印 98、99 等。
Thus it proves that concurrent updates of ArrayList will not give correct results.
从而证明 ArrayList 的并发更新不会给出正确的结果。
If i replace the ArrayList with a Synchronizedversion, the program outputs the correct results.
如果我用同步版本替换 ArrayList ,程序会输出正确的结果。
回答by Stan Fad
You could use instead of ArrayList();
:
您可以使用代替ArrayList();
:
Collections.synchronizedList( new ArrayList() );
or
或者
new Vector();
synchronizedList
as of me preferable because it's:
synchronizedList
对我来说更可取,因为它是:
- faster on 50-100%
- can work with already existing ArrayList's
- 快 50-100%
- 可以与已经存在的 ArrayList 一起使用