考虑以下代码并假设列表是同步列表。
List list = Collections.synchronizedList(new ArrayList());
if(!list.contains(element)){
list.add(element)
}
我知道上面的代码片段需要在外部同步(由锁保护)以使其完全线程安全。竞争条件在哪里出现?
考虑以下代码并假设列表是同步列表。
List list = Collections.synchronizedList(new ArrayList());
if(!list.contains(element)){
list.add(element)
}
我知道上面的代码片段需要在外部同步(由锁保护)以使其完全线程安全。竞争条件在哪里出现?
实际上有很多。一个列表在contains
被评估时可能会发生变化,当你到达add
某个人可能已经添加了该元素并且你再次添加它时。此外,如果没有同步,整个事情可能会以奇怪的方式分崩离析,因为您的线程可能会部分地、无序地或根本没有观察到其他线程的写入。
如果contains
and本身是原子的(同步的),那么在调用andadd
之间至少会有一场明确定义的竞争。contains
add
好吧,假设两个线程执行检查并且都进入条件语句。然后两者都会将相同的元素添加到列表中,这不是预期的行为。不是吗?
想象一下你有两个线程
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);
当您获取列表并重新保存它(如果保存到文件示例)之间,竞争条件将发挥作用。
示例:文件包含 A
读取文件将包含 AC 并且工作线程 1 已丢失
在我看来,竞争条件发生了,如果list.contains(element)
产生 false 并且另一个线程将在此之后添加元素,但在第一个线程中调用 add 之前。