为什么?假设每个线程在现代的 x86 CPU 上运行在自己的内核上,而内核不共享 L2缓存。如果只有一个线程,那么对象大部分时间都会保留在 L2缓存中。在两个线程都运行时,每当一个线程修改对象时,另一个线程将发现数据不在其 L2缓存中,因为另一个 CPU 使缓存线路无效。例如,在奔腾 D 上,这将导致代码以 FSB 速度运行,这远低于 L2缓存速度。
因为即使锁本身没有争用,也可能发生争用,所以当没有锁时也可能发生争用。例如,假设您的 CPU 支持32位变量的原子增量。如果一个线程不断地递增和递减一个变量,那么该变量在大多数时候都会在缓存中处于热状态。如果两个线程这样做,它们的缓存将争夺持有该变量的内存的所有权,并且由于缓存一致性协议操作以保护缓存线路的每个核心所有权,许多访问将变慢。
具有讽刺意味的是,锁通常是 减少争用。为什么?因为如果没有锁,两个线程可能操作同一个对象或集合,并导致大量的争用(例如,存在无锁队列)。锁往往会取消争用线程的调度,而允许非争用线程运行。如果线程 A 持有一个锁,而线程 B 需要相同的锁,则实现可以运行线程 C。如果线程 C 不需要这个锁,那么线程 A 和线程 B 之间将来的争用可以暂时避免。(当然,这里假设还有其他线程可以运行。如果整个系统能够取得有用进展的唯一方法是运行具有竞争力的线程,那么这种方法将无济于事。)