Java 并发性 – 线程安全性?
Java 并发性 – 线程安全性?
定义线程安全性非常棘手。 快速的 Google 搜索会发现许多类似的“定义”:
- 线程安全代码是即使许多线程同时执行也可以运行的代码。
- 如果一段代码仅以保证多个线程同时安全执行的方式操作共享数据结构,则它是线程安全的。
并且还有更多类似的定义。
您难道不认为上述定义实际上并没有传达任何有意义的信息,甚至还会增加一些混乱。 尽管不能完全排除这些定义,因为它们没有错。 但是事实是他们没有提供任何实际的帮助或观点。 我们如何在线程安全类和不安全类之间区分?“安全”甚至意味着什么?
什么是线程安全的正确性?
线程安全性的任何合理定义的核心是正确性的概念。 因此,在了解线程安全性之前,我们应该首先了解“正确性”。
正确性是指一个类符合其规范。
您将同意,良好的类规范将在任何给定的时间获取有关类状态的所有信息,并且如果对类状态进行了某些操作,则它是后置条件。 由于我们经常没有为我们的类编写足够的规范,我们怎么可能知道它们是正确的? 我们不能,但是一旦我们说服自己“ 该代码有效”,这仍然不会阻止我们使用它们。 这个**“代码置信度**”与我们许多人接近正确性的程度差不多。
将“正确性”乐观地定义为可以识别的东西之后,我们现在可以以一种不太循环的方式定义线程安全性:当从多个线程中访问时,如果类继续正确运行,则该类是线程安全的。
如果一个类在从多个线程访问时能正确运行,则无论该线程在运行时环境中对这些线程的执行进行调度或交织,并且在调用代码部分没有任何其他同步或其他协调的情况下,如果该类行为正确,则该线程是线程安全的。
如果此处对“正确性”的宽松使用使您感到困扰,则您可能更喜欢认为线程安全类在并发环境中比在单线程环境中不会被破坏。 线程安全类封装了所有需要的同步,因此客户端不需要提供自己的同步。
示例:无状态 Servlet
线程安全类的一个很好的例子是 java servlet,它没有字段和引用,也没有其他类的字段等。它们是无状态。
public class StatelessFactorizer implements Servlet
{
public void service(ServletRequest req, ServletResponse resp)
{
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
}
特定计算的暂态仅存在于局部变量中,这些局部变量存储在线程的栈中,并且只有执行线程才能访问。 一个线程访问StatelessFactorizer
不会影响另一线程访问同一StatelessFactorizer
的结果; 因为两个线程不共享状态,所以好像它们正在访问不同的实例。 由于线程访问无状态对象的动作不会影响其他线程中操作的正确性,因此无状态对象是线程安全的。
这就是围绕“什么是线程安全?”这个小而重要的概念的全部内容。