如果同时修改未受保护的Java集合,会发生什么情况



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

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

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

如果有一个写入程序,但并发读取不受保护,该怎么办?你还能崩溃并扰乱状态吗,或者只是产生不一致的读取?

这个实现是特定的,还是规范保证了一定级别的安全性和/或原子性?

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

根据您的用例,这可能会导致应用程序崩溃或死锁(当一个线程由于上述未捕获的异常而被JVM关闭时)。更糟糕的是,它可能会导致不可靠的问题和错误的结果,这些结果可能很难追踪。不过,它不会使JVM本身崩溃。

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

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

线程安全的糟糕之处在于,错误很少发生,而且很难再现。Java在很大程度上将线程的处理委托给了操作系统,因此当多个任务同时运行时,程序员无法控制操作系统如何以及何时暂停和切换线程。根据CPU是单核还是双核或四核,观察到的各种错误的频率可能不同。

并发错误很少会"使系统崩溃",更可能的问题是状态不一致。但是,如果您使用Iterator迭代一个集合,而另一个线程修改该集合,那么您将得到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 ;
 }

Iterator的实现试图检测它正在迭代的集合的并发修改,但这只是一种"快速失败"的最佳尝试。通过抛出异常使程序失败比具有不可预测的行为要好。javadocs声明:

请注意,通常情况下,不能保证快速故障行为说起来,在不同步的并发修改。失败快速操作投掷基于尽力而为的ConcurrentModificationException。因此编写依赖于此异常的程序是错误的其正确性:仅应使用ConcurrentModificationException以检测错误。

如果我们只是使用get从集合中读取数据,那么我们不会看到这种异常,但我们确实存在状态不一致的风险。有时这不是一个需要解决的问题。如果只有一个线程写入一个字段,而不是所有线程都能在该字段中看到最新的值,那么我认为只要你远离迭代器,你就应该没事了。

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

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

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

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

然而,如果缺乏同步可能导致JVM本身崩溃,我会非常惊讶。

最新更新