线程安全

定义线程安全是一件相当费劲的事。简单在谷歌上一搜,就出现大量的定义,比如:
1. 线程安全代码就是当多个线程同时执行时也能正常工作的代码
2. 如果以多个线程同时执行仍能保证正确的方式来管理共享的数据结构,那么我们就说代码块是线程安全的
还有一些类似的定义。
你不觉得上述定义等于啥也没说吗,甚至让你更加困惑。我们不能排除掉这些定义,毕竟它们不是错误的。但是事实上它们没有提供任何实际帮助和观点。如何区别线程安全类和非线程安全类?“安全”到底意味着什么?

线程安全中的正确性

所有合理的多线程定义都是“正确性”的概念,因此理解线程安全的前提就是这个“正确性”。

正确性意味着类符合它的规范

当特定的操作作用于类时,一个好的类规范将会包含任意时刻类的状态以及后置条件。由于我们常常不能给出完整的类规范,我们怎么可能知道它们是正确的呢?我们不知道,但即使这样,一旦我们确信“这些代码好使”,我们仍然会使用它们。这种 “代码自信” 跟我们想要的正确性很接近(This “code confidence” is about as close as many of us get to correctness.)
上面已经乐观地将“正确性”定义为可被识别的东西,现在让我们以一种直接的方式来定义线程安全:当被多个线程访问仍然保持正确的行为,那么,我们说这个类是线程安全的。

一个类是安全的,就是当它被多个线程访问,且无论运行环境如何调度,交替执行这些线,它都能保持正确的行为,并且调用方代码无需额外的同步或其他条件。

如果这种宽泛的“正确性”让你很烦躁,你可以将线程安全理解为相比于单线程环境,并发环境并没有给类本身带来更多破坏。线程安全类封装了所需的同步,因此客户端无需再提供。

示例:无状态的Servlet

线程安全类的一个很好示例就是java servlets,它没有任何字段及对其他类的引用等,它们是无状态的

public class StatelessFactorizer implements Servlet{
  public void service(ServletRequest req,ServletResponse resp){
    BigInteger i = extractFromRequest(req);
    BigInteger[] factors = factor(i);
    encodeIntoResponse(resp,factors);
  }
}

特定计算的瞬时状态仅存在于本地变量,而本地变量仅存储在执行线程的栈里,因此它只对执行线程可见。当一个线程访问StatelessFactorizer时,不影响另一个线程的访问,因为两个线程没有任何共享的状态,就像他们访问不同的实例。因为一个线程访问一个无状态类不影响另一个线程操作的正确性,所以无状态类是线程安全的。
以上就是关于这个很小但很重要的线程安全相关的概念
快乐学习!!


译者注:以下为一些精彩评论:
Ramakrishna:
如果参数类型是基本类型,那么每个线程在自己的栈里维护这些变量,如果一个方法引用另一个对象作为参数,那么每个线程仍然在自己线程栈中维护这些对象的状态吗?
Rajesh:
对象的状态总是保存在堆上而非栈,如果你在方法内部引用其他对象,在这种情况下每个线程都会有自己的一份拷贝,因此是线程安全的。

发表评论

邮箱地址不会被公开。 必填项已用*标注