我正在尝试在一个线程上迭代循环,如下所示:
for (UnitTask task : chain) {
g.drawLine((int) task.getLocation().getX(), (int) task.getLocation().getY(), (int) currentPos.getX(), (int) currentPos.getY());
g.fillOval((int) task.getLocation().getX() - 2, (int) task.getLocation().getY() - 2, 5, 5);
currentPos = task.getLocation();
}
但是,我有另一个可以添加到此对象的线程(Swing事件线程)。因此,ConcurrentModificationException
。我试着用synchronized (chain) { ... }
包围代码来获取锁定,但我仍然得到错误。
作为一个Java同步新手,我有点困惑为什么。我希望这可以使循环线程安全,但显然,它不是。
有趣的是,chain
是自定义类的一个实例,但它只是LinkedList
的一个薄包装器。列表本身是私有的,外部类没有办法直接检索它(有明确添加/删除对象的方法),所以我不希望这会影响结果。
答案 0 :(得分:10)
的含义
synchronized (c) {
... code that uses c ...
}
是
c
解锁c
c
因此,如果您在线程中进行同步,那么您的线程将等待c
解锁,然后潜入。
现在,如果你不同步修改c
的其他线程上的代码,那么该代码将继续并修改{{ 1}}无需等待锁定。在一个线程中同步块不会使另一个线程等待锁定。如果另一个线程有一行,如
c
不在同步块中,无论如何都会添加。这是您的例外原因。这也是为什么你看到异常的原因,即使你把代码放在你的线程中的同步块:你的代码是“按规则播放”,但另一个线程不能少关心。
请注意同步长时间运行的代码。正如Stephen C所说,你最好使用并发集合类型。
答案 1 :(得分:3)
同步不一定有帮助。
基本上问题是您使用的集合类型在迭代进行时不允许修改集合(除非通过迭代器的remove
方法...如果支持) 。这不是线程/同步问题本身。 (如果你试图通过同步来解决它,你可能会引入另一个问题。)
如果您希望能够同时进行迭代和修改,则需要使用其他集合类型,例如ConcurrentLinkedDeque而不是LinkedList
。
如果迭代和写入发生在不同的线程上,那么不应该同步阻止写入直到迭代完成?或者我错过了什么?
问题在于如何实现同步:
如果您未在LinkedList
版本中明确进行某种同步,则不会为您执行任何同步。
如果您使用由Collections.synchronizedXxx
方法之一创建的同步包装器,那么这些方法的javadoc清楚地表明包装器返回的Iterator
对象iterator()
{{ 1}}方法未同步。
如果您正在手动执行同步,则必须确保所有内容都在同一个互斥锁上进行同步。并且该锁必须在迭代期间保持在该互斥锁上...而不仅仅是调用iterator()
。
请注意,如果长时间持有锁(例如,在迭代长列表时),这可能会阻止需要长时间更新列表的其他线程。这种事情可能是并发瓶颈,可能(在最坏的情况下)将系统性能降低到单个处理器的速度。
ConcurrentXxx
类通常通过放宽迭代器生成的序列的一致性保证来避免这种情况。例如,在开始迭代后,您可能看不到添加到集合中的元素。