3.6 Java标准库实例
在多线程环境中,当遍历一个集合(Collection,如java.util.Vector)对象时,即便被遍历的对象本身是线程安全的,开发人员仍然不得不引入锁,以防止遍历过程中该集合的内部结构被其他线程改变(如删除或者添加了一个新的元素)而导致出错,如清单3-7所示。
清单3-7 遍历线程安全的集合时加锁
为了保证线程安全而在遍历时对集合对象加锁,但这在某些情况下可能并不合适,比如系统中对该集合的添加和删除操作的频率远比遍历操作的频率要高。JDK 1.5中引入的类java.util.concurrent.CopyOnWriteArrayList应用了Immutable Object模式,使得对CopyOnWriteArrayList实例进行遍历时不用加锁也能保证线程安全。当然,CopyOnWriteArrayList也不是“万能”的,它是专门针对遍历操作比添加和删除操作更加频繁的场景设计的。CopyOnWriteArrayList的源代码(骨架)如清单3-8所示。
清单3-8 JDK类CopyOnWriteArrayList的源代码(骨架)
从清单3-8中的代码可见,CopyOnWriteArrayList的内部维护了一个名为array的实例变量,其用于存储集合的各个元素。在集合中添加一个元素的时候,CopyOnWriteArrayList会生成一个新的数组,并将集合中现有的元素都复制给新的数组,然后将新的数组的最后一个元素设置为要添加的元素。这个新的数组会直接被赋值给array实例变量。在这里,实例变量array引用的数组就是一个等效的不可变对象[2],其内容一旦确定下来就不再改变。因此,在遍历CopyOnWriteArrayList以维护各个元素的时候,直接根据array实例变量生成一个Iterator实例即可,无须加锁[3]。