Java中迭代SynchronizedList子列表时应使用何种同步方式?
SynchronizedList子列表迭代的正确同步方式
问题背景
当通过Collections.synchronizedList()创建的同步列表调用subList()方法得到子列表后,迭代该子列表时需要采用何种同步方式?这是一个容易踩坑的线程安全问题。
官方文档的基础规则
根据Collections.synchronizedList()的官方文档说明,迭代同步列表时必须手动在该列表对象上进行同步,官方给出的示例如下:
List list = Collections.synchronizedList(new ArrayList()); // ... synchronized (list) { Iterator i = list.iterator(); // 必须在同步块内执行迭代器创建与遍历 while (i.hasNext()) foo(i.next()); }
子列表的锁对象误区
但需要注意的是,SynchronizedList的子列表不会以自身作为互斥锁——从源码实现来看,子列表会复用原同步列表的锁对象。如果像下面这样直接对子列表对象加锁,属于错误的同步方式:
List subList = synchronizedList.subList(2, 5); synchronized (subList) { // 错误:子列表并非实际的锁对象 Iterator i = subList.iterator(); while (i.hasNext()) foo(i.next()); }
这种写法无法保证线程安全,因为其他线程操作原列表或子列表时,用的是原列表的锁,当前同步块的锁起不到互斥作用。
正确的同步方式
迭代子列表时,必须以原同步列表的对象作为锁,示例代码如下:
List originalSyncList = Collections.synchronizedList(new ArrayList()); List subList = originalSyncList.subList(2, 5); // ... synchronized (originalSyncList) { // 使用原同步列表作为锁对象 Iterator i = subList.iterator(); while (i.hasNext()) foo(i.next()); }
文档的疏漏点
目前官方文档仅明确了原同步列表的迭代同步要求,并未提及子列表的这一特殊情况。这确实是文档的一处不足,因为开发者很容易按照惯性用子列表本身加锁,进而引发线程安全问题,建议官方文档补充这一细节说明。
内容的提问来源于stack exchange,提问作者peremeykin
相关产品推荐
相关产品推荐

