一个程序怎么可能只包含线程安全的类,而不包含线程安全的类?

How is it possible for a program to contain exclusively thread-safe classes, but not be thread-safe?

提问人:Coder-Man 提问时间:5/10/2018 最后编辑:Coder-Man 更新时间:7/25/2018 访问量:121

问:

我正在阅读“实践中的 java 并发”,作者说:“完全由线程安全类组成的程序可能不是线程安全的”。这怎么可能?我似乎不明白,有人可以举个例子吗?

多线程并 与语言无关

评论

0赞 Evk 5/10/2018
例如:即使字典本身是线程安全的,也不是线程安全的。if (safeDictionary.ContainsKey(x)) { y = safeDictionary[x]; ... }
2赞 user4581301 5/10/2018
这里有一个很好的 Java 相关示例:为什么 Java Vector 类被认为过时或已弃用?

答:

10赞 Andy Turner 5/10/2018 #1

例如,类上的单个方法是线程安全的,但如果调用多个方法,则它们不是原子的。例如

if (!threadSafeCollection.contains(thing)) {
  threadSafeCollection.add(thing);
}

如果另一个线程将该线程的 和 调用之间的集合添加到该线程的集合中,则可能会产生不正确的结果。containsadd

评论

0赞 Coder-Man 5/10/2018
但是,如果它们不是原子的,它们怎么可能是线程安全的呢?在我的理解中,如果受多线程访问约束的方法执行非原子操作,那么它就不是线程安全的。
0赞 user4581301 5/10/2018
@POrekhov取决于如何定义线程安全。通常,这仅扩展到单个操作。您可以保证在插入时没有人会删除,但如果必须插入两次,则需要额外的保护。
0赞 Daniel Langr 5/10/2018
@POrekhov “但是,如果它们不是原子的,它们怎么可能是线程安全的呢?”简单地说,例如,通过在内部使用互斥锁。但是,在 Andy 的示例中,此互斥锁在操作之间解锁,这就是重点。
0赞 Mats Petersson 5/10/2018
contains本身是线程安全的。 本身是线程安全的。但是两个尝试添加的线程仍然可以同时添加到集合中,因为两者都检查 17 是否在集合中(不是),然后都调用 ,使 17 出现两次。为了解决这个问题,你需要一个函数,它以一种线程安全的方式进行检查和添加。add1717addaddIfNotContains
1赞 Mats Petersson 5/10/2018
@POrekhov:在某个地方,你需要这个程序来做一些事情。该代码还需要考虑其操作的线程安全性。
0赞 user10129738 7/25/2018 #2

为了更清楚地说明这个问题。

来自 JCIP(声明 1):

线程安全程序是完全由线程安全类构造的吗?不一定,完全由线程安全类组成的程序可能不是线程安全的,而线程安全程序可能包含非线程安全的类。页码 17

B Goetz 将什么定义为线程安全?(声明2)

如果一个类在从多个线程访问时行为正确,则该类是线程安全的,而不管运行时环境如何安排或交错执行这些线程,并且调用代码没有额外的同步或其他协调。页码 18

如果我们将语句 1 解释为对完全线程安全类进行调用的类本身不计为程序中类集的一部分,那么我对这两个语句的解释是有道理的。然后,人们可以构造一个带有检查然后行动问题的程序,这个操作应该是原子的,但不是,例如,在上面安迪·特纳(Andy Turner)的回答中。