【问题标题】:Synchronizing threads in Java在 Java 中同步线程
【发布时间】:2012-02-25 09:53:27
【问题描述】:

我有两个线程修改同一个对象,假设是 MyObject,因此同步了该对象。但在其中一个线程中,另一个对象被修改,因此必须调用 MyObject。

public void run(){
   synchronized(MyObject){
             ...
            anotherObject.modify();//<----calls MyObject
             ...
    }
 }

这会导致 ConcurrentModificationExceptions。我不知道如何解决这个问题。如果我不同步,我会在两个线程都尝试调用 MyObject 时遇到异常。我该如何解决这个问题?

更新:代码适用于 Android 设备。我之前没有提到它,因为这里没有 Android 特定的对象在起作用。 LogCat 输出不是很有帮助

02-03 02:47:43.660:错误/AndroidRuntime(5258):未捕获的处理程序:线程主因未捕获的异常而退出 02-03 02:47:43.670: 错误/AndroidRuntime(5258): java.util.ConcurrentModificationException 02-03 02:47:43.670: 错误/AndroidRuntime(5258): 在 java.util.AbstractList$SimpleListIterator.next(AbstractList.java:64) 02-03 02:47:43.670: ERROR/AndroidRuntime(5258): at com.jjg.myapp.gameunit.findEnemy(MoveableUnit.java:656)//

我试图同步的对象本质上是一个名为 gs 的 GameState。它包含各种 ArrayList、数组和其他对象。 gs 不是静态的。

上面出现问题的方法是:

for(GameUnit gu : this.getBase().getGameState().getAllUnits()){//<---this is the problem line.
//do some stuff...
 }

getAllUnits 返回 GameUnits 的 ArrayList(包括调用该方法的 GameUnit - 我不需要迭代器,因为没有对象被删除或创建)。

【问题讨论】:

  • 向我们展示真实的代码,以及异常的堆栈跟踪。 ConcurrentModificationException 可能与多线程无关。
  • 细节太少,无法提供与您的情况相关的具体建议。向我们展示您的实际代码。
  • 我更新了它。我希望更新的信息有用

标签: java android multithreading synchronized concurrentmodification


【解决方案1】:

您确实在问与此相同的问题:Thread-safe iteration over a collection

最简单的解决方案是在迭代之前复制列表:

for(GameUnit gu : new ArrayList<GameUnit>(this.getBase().getGameState().getAllUnits()))

或者,您可以在集合本身上进行同步(请注意,要使其正常工作,您还必须在修改“所有单位”列表的任何位置进行同步):

ArrayList<GameUnit> units = this.getBase().getGameState().getAllUnits())
synchronized (units) {
  ...
}

【讨论】:

    【解决方案2】:

    如果在使用迭代器时修改了集合,则通常会抛出 ConcurrentModificationException。 -- 所以我的你被名字误导了,只是搜索错误的问题。

    【讨论】:

    • 我认为使用迭代器可以防止 Con.Mod.Ex. 的?我很困惑。
    • @JJG:实际上是iterator一旦检测到修改就会抛出这样的异常。您可能会混淆它与您可以使用迭代器从集合中remove这一事实
    • 顺便说一句:foreach 循环也是基于交互器的。
    • @JJG,来自 Javadoc:“如果列表在迭代器创建后的任何时候在结构上被修改,除了通过迭代器自己的 remove 或 add 方法之外,迭代器将抛出 ConcurrentModificationException 。”是什么让您认为迭代器避免 CME?
    【解决方案3】:

    我有两个线程在修改同一个对象,比如说 MyObject,然后 所以已经同步了对象。

    不,你没有。只有 1 个线程正在修改 MyObject 另一个被阻止。

    但是在其中一个线程中,另一个对象被修改了,这样做 必须调用 MyObject。

    拥有锁的线程将再次运行,因为锁是租用的。

    正如 cmets 所说,您发布的代码并未显示您为获得该异常所做的实际操作。

    MyObjectCollection 吗?您是否在其他地方同时修改此集合,即没有正确同步?
    或者也许是迭代?
    在这种情况下,你会得到你说的异常

    【讨论】:

      【解决方案4】:

      你不能这样做,这是循环逻辑。

      您要么必须完全摆脱线程并确定明确的评估顺序,要么以其他方式共享数据。

      一种可能的策略是将调用“anotherObject.modify”放到一个单独的可运行线程中,如下所示:

        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
             anotherObject.modify();
          }
        });
      

      【讨论】:

        猜你喜欢
        • 2011-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多