1

考虑以下代码并假设列表是同步列表。

List list = Collections.synchronizedList(new ArrayList());

if(!list.contains(element)){
       list.add(element)
}

我知道上面的代码片段需要在外部同步(由锁保护)以使其完全线程安全。竞争条件在哪里出现?

4

5 回答 5

3

实际上有很多。一个列表在contains被评估时可能会发生变化,当你到达add某个人可能已经添加了该元素并且你再次添加它时。此外,如果没有同步,整个事情可能会以奇怪的方式分崩离析,因为您的线程可能会部分地、无序地或根本没有观察到其他线程的写入。

如果containsand本身是原子的(同步的),那么在调用andadd之间至少会有一场明确定义的竞争。containsadd

于 2012-06-29T13:08:27.997 回答
2

好吧,假设两个线程执行检查并且都进入条件语句。然后两者都会将相同的元素添加到列表中,这不是预期的行为。不是吗?

于 2012-06-29T13:06:27.103 回答
2

想象一下你有两个线程

A: if(!list.contains(element)){ // false
B: if(!list.contains(element)){ // false
A:     list.add(element) // add once
B:     list.add(element) // add a second time.

当然,简单的解决方案是使用 Set。例如

Set<E> set = Collections.newSetFromMap(new ConcurrentHashMap<E, Boolean>());

set.add(element);
于 2012-06-29T13:40:50.207 回答
0

当您获取列表并重新保存它(如果保存到文件示例)之间,竞争条件将发挥作用。

示例:文件包含 A

  • 线程 1 获得 A
  • 线程 1 将 B 添加到 A
  • 线程 2 获得 A
  • 线程 1 将 AB 保存到文件
  • 线程 2 将 C 添加到 A
  • 线程 2 将 AC 保存到文件

读取文件将包含 AC 并且工作线程 1 已丢失

于 2012-06-29T13:07:07.400 回答
0

在我看来,竞争条件发生了,如果list.contains(element)产生 false 并且另一个线程将在此之后添加元素,但在第一个线程中调用 add 之前。

于 2012-06-29T13:07:59.907 回答