如果同时修改不受保护的Java集合会发生什么?

时间:2013-07-13 09:51:08

标签: java concurrency jvm

http://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html

  

请注意,此实现未同步。如果多个线程同时访问链接列表,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。

如果不这样做,会发生什么?你可以崩溃JVM,导致异常或只是产生不一致的状态吗?

如果有一个编写器但并发读取不受保护怎么办?你仍然可以崩溃并弄乱国家,或只是产生不一致的阅读?

这是特定于实现的,还是规范保证了某种程度的安全性和/或原子性?

4 个答案:

答案 0 :(得分:1)

在多线程环境中使用非同步集合会导致脏读(数据状态不一致)和ConcurrentModificationException等问题(主要是当一个线程修改了集合的内容而另一个线程正在迭代它时)。

根据您的使用情况,这可能会导致您的应用程序崩溃或deadlock(由于上面提到的未被捕获的异常,JVM关闭了一个线程)。更糟糕的是,它可能会导致狡猾的问题和可能难以追查的错误结果。但它不会使JVM本身崩溃。

我建议您查看java.util.concurrent包。您将找到各种各样的线程安全,高效的集合。它们中的大多数都具有弱一致的迭代器,在迭代器创建时或之后的某个时刻返回反映集合状态的元素。这意味着它们不会抛出ConcurrentModificationException,并且可能与其他操作同时进行。

有关Java内存模型及其保证的信息,请参阅this(非常值得一读!)。

答案 1 :(得分:0)

您可能不会崩溃应用程序,但您的不同线程将遇到DIRTY_READ问题。

答案 2 :(得分:0)

关于线程安全的令人讨厌的事情是错误很少发生并且难以重现。 Java主要将线程的处理委托给操作系统,因此当多个任务同时运行时,程序员无法控制操作系统如何以及何时暂停和切换线程。观察到的各种错误的频率可能会有所不同,具体取决于CPU是单核还是双核或四核。

并发错误很少会“使系统崩溃”,更可能的问题是状态不一致。但是,如果您使用迭代器迭代集合,而另一个线程修改集合,那么您将获得ConcurrentModificationException。例如:

 Set<String> words; //a field that can be accessed by other threads.
 // may throw ConcurrentModificationException
 public ArrayList<String> unsafeIteration()  {
   ArrayList<String> longWords = new ArrayList<>();
   for(String word : words) {
      if(word.length()>4)
        longWords.add(word);
   }
   return longWords ;
 }

迭代器的实现试图检测它正在迭代的集合的并发修改,但这只是“尽快失败”的尽力尝试。通过抛出异常导致程序失败,比具有不可预测的行为好得多。 javadocs制作此免责声明:

  

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

如果我们只是使用get从集合中读取数据,那么我们不会看到此异常,但我们确实存在状态不一致的风险。有时这可能不是需要修复的问题。如果只有一个线程写入一个字段并且所有线程总是在该字段中看到最新的最新值并不重要,我认为只要你不清楚迭代器就应该没问题。

答案 3 :(得分:0)

集合框架的根类Javadoc,java.util.Collection写道:

  

由每个集合决定自己的同步策略。在实现没有更强的保证的情况下,未定义的行为可能是由于另一个线程正在变异的集合上的任何方法的调用引起的;这包括直接调用,将​​集合传递给可能执行调用的方法,以及使用现有迭代器来检查集合。

“未定义的行为”意味着集合可以随心所欲,集合框架的整个javadoc都是无效的。例如,元素在被删除后可能仍然存在于集合中,或者在添加后不存在。例如,如果线程1添加到HashMap并在线程2插入内容时触发调整大小,则线程2中的插入可能会丢失。

但是,如果缺少同步可能会导致JVM本身崩溃,我会非常惊讶。