从并发修改异常中恢复

时间:2015-03-12 19:52:35

标签: java linux multithreading java-8

所以我坚持使用一个线程敌对单例实现,它将Iterator返回给HashSet。 我有两个线程 - 有时 - 同时访问此迭代器以加载数据。我称之为luckyThread和unluckyThread。 ONE (unluckyThread)抛出ConcurrentModificationException。

问题:可以安全地假设其他线程一切顺利吗? 具体来说:luckyThread加载的数据是否没有腐败? (发生这种情况的时间系统一直很好,除了不幸的线程) 不要认为这个问题需要任何代码示例,但如果需要,我很乐意提供它们。

更新:(没有详细说明)只要其中一个线程加载一个干净的数据集,系统就可以了。而且不用说我修复了这个问题,但这让我想到从这些异常中恢复,我没有在网上找到任何具体内容。

4 个答案:

答案 0 :(得分:5)

如果你查看ConcurrentModificationException的文档,它会明确指出:

  

请注意,通常情况下无法保证快速失败的行为   说话,在场的情况下不可能做出任何硬性保证   不同步的并发修改。失败快速操作投掷   ConcurrentModificationException是尽力而为的。因此,它   编写一个依赖于此异常的程序是错误的   它的正确性:只能使用ConcurrentModificationException   检测错误。

相反,您可能应该使用其他一些机制来确保没有并发访问(例如在访问底层HashSet时使用synchronized - 块与单例)。

答案 1 :(得分:3)

抛出异常是因为后备存储已更改,这使得该存储上的迭代器的任何使用都容易受到异常的影响。如果写得不好,甚至可以在单线程应用程序中进行。在你的情况下,你的两个线程都没有过于幸运,因为两者都会在发生变化时遇到这种异常。

即使没有更改你的底层HashSet,让两个线程访问你的迭代器也会导致不确定的行为,因为两者都会改变迭代器的内部状态,更不用说在最好的情况下每个线程都会抓取不同的项目从你的集合。

代码本身并不安全,必须重写以使用线程安全Set并且不共享线程之间的迭代器。

答案 2 :(得分:3)

这绝对不安全。您目前正在查看最佳案例场景:一个线程获得ConcurrentModificationException。它可能比这更糟糕。并发访问时未定义HashSet的行为。我不确定HashSet上的迭代器有多稳定,但快速查看来源让我觉得它可能会出错。如果在迭代期间重新键入键,则很有可能以无限循环结束。

结论:要么同步对迭代器的访问,要么创建集合的副本(在同步块中),要么更改为线程安全集合。

答案 3 :(得分:0)

另一种方法是使用ConcurrentHashMap代替HashMap。在多线程应用程序中访问ConcurrentHashMap时,您不需要具有同步块。