【问题标题】:Java and Synchronizing two threadsJava和同步两个线程
【发布时间】:2012-05-12 10:59:12
【问题描述】:

我有两个线程修改相同的对象。这些对象是ArrayList(不是向量)中的自定义、非同步对象。我想让这两个线程很好地协同工作,因为它们是同时被调用的。

这是线程 1 中唯一重要的方法。

public void doThread1Action() {
  //something...
  for(myObject x : MyArrayList){
    modify(x);
  }
}

这是线程2中的一个方法:

public void doThread2Action() {   
  //something...
  for(myObject x : MyArrayList){
    modifyAgain(x);
  }
}

目前,在测试时,我偶尔会遇到`ConcurrentModificationExceptions`。 (我认为这取决于线程 1 在线程 2 尝试修改对象之前完成迭代的速度。)

我是否认为通过简单地将synchronized 附加到这两个方法的开头,线程将以同步的方式一起工作,而不是尝试访问ArrayList?还是应该将ArrayList 更改为Vector

【问题讨论】:

    标签: java synchronization


    【解决方案1】:

    ConcurrentModificationException 并非源于修改集合中的对象,而是源于在迭代器处于活动状态时从集合中添加/删除。

    共享资源是集合,必须有第三种方法使用和添加/删除。要获得正确的并发性,您必须在访问它的所有方法中同步对集合资源的访问。

    为避免同步块过长,一种常见的模式可能是将集合复制到同步块中,然后对其进行迭代。如果您这样做,请注意您首先谈论的问题(对象的并发修改)再次出现 - 但这次您可以锁定另一个资源。

    【讨论】:

      【解决方案2】:

      您不需要同步对列表的访问,只要您不对其进行结构性修改,即只要您不从列表中添加或删除对象。你也不应该看到ConcurrentModificationExceptions,因为只有在你对列表进行结构性修改时才会抛出这些。

      因此,假设您只修改列表中包含的对象,但不添加或删除或重新排序列表中的对象,则可以在修改时对包含的对象进行同步,如下所示:

      void modifyAgain(MyObject x) {
          synchronized(x) {
              // do the modification
          }
      }
      

      我不会在 modifyAgain() 方法上使用 synchronized 修饰符,因为这不允许同时修改列表中的两个不同对象。

      另一个线程中的modify()方法当然必须和modifyAgain()一样实现。

      【讨论】:

        【解决方案3】:

        您需要在同一个锁上同步访问集合,因此仅在方法上使用同步关键字(假设它们在不同的类中)将锁定两个不同的对象。

        所以这里是你可能需要做的一个例子:

        Object lock = new Object();
        
        public void doThread1Action(){
        
        //something...
            synchronized(lock){
                for(myObject x : MyArrayList){
                   modify(x);
            }
        
        }
        
        public void doThread2Action(){
        
        //something...
            synchronized(lock){
                for(myObject x : MyArrayList){
                    modifyAgain(x);
            }
        
        }
        

        您也可以考虑使用CopyOnWriteArrayList 而不是Vector

        【讨论】:

          【解决方案4】:

          我猜你的问题与ConcurrentModificationException 有关。这个类在它的 Java 文档中说:

          /**
          * 检测到的方法可能会抛出此异常 并发
          * 修改对象时不进行此类修改 允许的。

          */

          在您的情况下,问题是列表中的迭代器并且可能会被修改。我想通过以下实现你的问题将是唯一的:

          public void doThread1Action()
          {
              synchronized(x //for sample)
              {
                 //something...
                 for(myObject x : MyArrayList)
                 {
                    modify(x);
                 }
              }
          }
          

          然后:

          public void doThread2Action()
          {
              synchronized(x //for sample)
              {
                 //something...
                 for(myObject x : MyArrayList)
                 {
                    modifyAgain(x);
                 }
              }
          }
          

          为了获得更好的结果,我希望有人纠正我的解决方案。

          【讨论】:

            猜你喜欢
            • 2016-10-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-12
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多