准确定义
线程安全是件棘手的事情。学术性的定义过于复杂,而本文只想给出一些实践方面的指导。Google给出的答案是:
. . .
can be called from multiple program
threads without unwanted interactions between the threads.(当多个线程访问时没有不恰当的交互)
. . .may be called by more than one thread at a time without requiring any other action on the caller's part.(能同时被多个线程同时访问,并且访问者不需要多余的动作)
看到这样的答案时,我想你们都已经被彻底弄晕了。听起来就像是“多个线程能安全访问的类就是
线程安全的类”。好吧,我承认这样的答案绝对是正确的,但是似乎一点用也没有。那么线程安全到底是什么意思?
任何对于线程安全的定义,其实质是对正确性的定义。这对于
理解线程安全非常重要。
所谓正确性,指的是类符合它的设计规范。比如像这样的定义就是一个良好的规范:对象的状态,以及做了一些操作后的状态都不可变(比如用final修饰)。在实际开发中,我们很少对类做具体的规范。通常我们认为代码能正确跑起来或者能够通过测试,这段代码就是正确的。采用这种乐观主义的精神,我们可以给线程安全下这样的定义:
不管多个线程是怎样的执行顺序和优先级,或是wait,sleep,join等控制方式,,如果一个类在多线程访问下运转一切正常,并且访问类不需要进行额外的同步处理或者协调,那么我们就认为它是线程安全的。
如果一个类在单线程环境下是不正确的,那显然不可能是线程安全的。我们在实现一个类时,对它进行的方法调用和读写公用值都应该遵循定义好的规范。类的任何实例以及方法调用,都不应当使得类处于无效的状态。
线程安全的类应当封装了所有必要的同步操作,调用者无需额外的同步。
让我们看一个无状态的servlet:
@ThreadSafe
public class StatelessFactorizer implements Servlet {
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
}
和大多数的servlet一样,StatelessFactorizer是无状态的:它没有域值,也没有指向其他对象域值的引用。临时的状态用临时变量来修饰,调用的时候都处于当前调用线程的作用域内。多个调用StatelessFactorizer类的线程之间无法互相影响,因为它们没有共享的状态。既然调用无状态类的线程无法影响其他调用线程,我们就称:
无状态的类永远是线程安全的
事实上,大多数的servlet都是线程安全的,除非它需要记录一些状态的时候,才需要的额外的线程安全处理。