当并发使用一个类的方法时,类的行为方式是该类与其客户端建立的约定的重要组成部分。如果这方面的内容你没有在类的文档中记录下来,使用这个类的用户将被迫做出假设。如果这些假设是错误的,这样得到的程序就可能缺少足够的同步(第78项)或者过度同步(第79项)。无论哪种情况,都可能导致严重的错误。
你可能听到过这样的说法:通过查看文档中是否出现synchronized修饰符,你可以确定一个方法是否是线程安全的。这种说法从几个方面来说都是错误的。在正常的操作中,Javadoc并没有在它的输出中包含synchronized修饰符,是有好的理由的。因为在一个方法中出现synchronized修饰符,这是个实现细节,并不是API的一部分 。它并不一定表明这个方法是线程安全的。
而且,“使用synchronized修饰符声明的存在就足以用文档说明线程的安全性”的这种说法说体现了一种误解,即线程安全是全有或全无的属性。实际上,线程安全性有多种级别。一个类为了可以被多个线程安全地使用,必须在文档中清楚地说明它所支持的线程安全性级别 。下面的列表概括了线程安全性的几种级别。这份列表并没有涵盖所有的可能,而只是些常见的情形:
-
不可变的(immutable) ——这个类的实例是不变的。所以,不需要外部的同步。这样的例子包括String、Long和BigInteger(第17项)。
-
无条件的线程安全(Unconditionally thread-safe) ——这个类的实例是可变的,但是这个类有着足够的内部同步,所以,它的实例可以被并发使用,无需任何外部同步。其例子包括AtomicLong和ConcurrentHashMap。
-
有条件的线程安全(Conditionally thread-safe) ——除了有些方法为进行安全的并发使用而需要外部同步之外,这种线程安全级别与无条件的线程安全相同。这样的例子包括Collections.synchronized包装返回的集合,它们的迭代器(iterator)要求外部同步。
-
非线程安全(not thread-safe) ——这个类的实例是可变的。为了并发地使用它们,客户端必须利用自己选择的外部同步包围每个方法调用(或者调用序列)。这样的例子包括通用的