【问题标题】:How to correctly use synchronized?如何正确使用同步?
【发布时间】:2011-11-06 00:25:27
【问题描述】:

这段代码:

synchronized (mList) {
    if (mList.size() != 0) {
        int s = mList.size() - 1;
        for (int i = s; i > 0; i -= OFFSET) {
            mList.get(i).doDraw(canv);
        }
        getHead().drawHead(canv);
    }
}

随机抛出 AIOOBE。根据我的阅读,同步应该可以防止这种情况发生,那么我做错了什么?

编辑:

AIOOBE = 数组索引越界异常 代码不完整,减少到需要的内容。但是为了让你开心,OFFSET 是 4,想象一下有一个 for 循环在开头添加了一点数据。第二个线程读取和/或修改列表。

编辑 2:

我注意到在绘制列表并且当前游戏结束时会发生这种情况。清空列表时,绘制线程尚未绘制所有元素。有没有办法让游戏等待清空列表,直到它为空?

编辑 3:

我刚刚注意到我不确定这是否是多线程问题。似乎我只有 2 个线程,一个用于计算和绘图,一个用于用户输入。我必须比我想象的多研究一下。

【问题讨论】:

  • IOORE?请扩展为一个完整的异常名称 - 我敢说我们可以在一段时间后弄清楚你的意思,但如果你真的告诉我们开始,那会简单得多。
  • 您的代码看起来不完整。数据在哪里添加到列表中(并且,就此而言,删除)? OFFSET 是什么?
  • 如果你说的是ArrayIndexOutOfBoundsException,我通常缩写为aioobe ;-)
  • 只有我可以看到 doDraw(canv) 从同一个列表中删除多个对象,然后如果 OFFSET = 1 则下一个 i 将不存在
  • 与您的问题无关:您确定要i > 0 而不是i >=0

标签: java android synchronized synchronized-block


【解决方案1】:

你在做的事情看起来是对的......但仅此而已:

  1. 同步的对象无关紧要,不一定是列表本身。
  2. 重要的是,在访问共享资源时,所有线程是否始终在同一个对象上同步。
  3. 对 SWING(或其他图形库)的任何访问都必须在 AWT 线程中进行。

您的编辑:

我注意到在绘制列表并且当前游戏结束时会发生这种情况。清空列表时,绘制线程尚未绘制所有元素。有没有办法让游戏等待清空列表,直到它为空?

我认为您的意思是“……等待清空列表,直到绘图完成。”只需在同一个锁上同步执行此操作的代码(即在您的情况下为列表本身)。

再次重申:必须以某种方式保护对共享资源的任何访问。您似乎只是在这里使用synchronized,而不是在您清空列表的地方。

【讨论】:

  • 是的,第 2 点非常重要。 @SBoss 我们看不到其余代码也使用了mList,但每个 访问或修改mList 的代码段必须受到synchronized(mList) 的保护。只同步一部分代码是完全没用的。
  • 1.所以我可以写同步()? 2. 谢谢,不知道。我会检查我的代码。 3. AWT?
  • @SBoss 不,如果您在实例 MyClass class1MyClass class2 周围有 synchronize(this) 周围的 mList,则 this 锁定在两个不同的锁上,并且不会有同步就mList而言。
  • @SBoss 回答 Edit2:您必须在清空列表的最终游戏中同步代码块!每次使用mList时都要同步你的代码;否则你会有其他错误。
【解决方案2】:

安全的解决方案是只允许一个线程创建对象,在游戏开始后从列表中添加和删除它们。

我自己遇到了随机 AIOOBE 错误的问题,没有同步可以正确解决它,而且它减慢了用户的响应速度。

我的解决方案现在稳定且快速(此后从未有过 AIOOBE)是通过将触摸的标志和坐标设置到持久变量中,让 UI 线程通知游戏线程创建或操作对象。

由于游戏线程每秒循环大约 60 次,这被证明足以从 UI 线程中获取消息并执行某些操作。

这是一个非常简单的解决方案,效果很好!

【讨论】:

  • 列表仅从 1 个线程创建/修改/清空。 UI 线程只读取/绘制对象。
  • 在我的游戏中,UI线程只通知游戏线程该做什么,仅此而已。由于我只有一个线程来执行逻辑,因此我可以很好地控制何时以及如何完成事情。我不知道这是否适合您的应用程序,但这是一种方法。
【解决方案3】:

我的建议是使用BlockingQueue,我认为您也在寻找这个解决方案。你怎么能做到?它已经在 javadoc 中通过示例显示:)

 class Producer implements Runnable {
   private final BlockingQueue queue;
   Producer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { queue.put(produce()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   Object produce() { ... }
 }

 class Consumer implements Runnable {
   private final BlockingQueue queue;
   Consumer(BlockingQueue q) { queue = q; }
   public void run() {
     try {
       while (true) { consume(queue.take()); }
     } catch (InterruptedException ex) { ... handle ...}
   }
   void consume(Object x) { ... }
 }

 class Setup {
   void main() {
     BlockingQueue q = new SomeQueueImplementation();
     Producer p = new Producer(q);
     Consumer c1 = new Consumer(q);
     Consumer c2 = new Consumer(q);
     new Thread(p).start();
     new Thread(c1).start();
     new Thread(c2).start();
   }
 }

对您有益的是,您不必担心同步您的mListBlockingQueue 提供 10 种特殊方法。您可以在文档中检查它。 javadoc 中的几个:

BlockingQueue 方法有四种形式,用不同的方式处理不能立即满足但可能在未来某个时间点满足的操作:一种抛出异常,第二种返回特殊值(null 或 false,取决于操作),第三个无限期阻塞当前线程,直到操作成功,第四个阻塞仅在给定的最大时间限制后放弃。

为了安全起见:我对 android 没有经验。所以不确定android中是否允许所有java包。但至少应该是:-S,我希望。

【讨论】:

    【解决方案4】:

    您正在获取 Index out of Bounds 异常,因为有 2 个线程在列表上操作并且执行错误。 您应该在另一个级别进行同步,以这样一种方式,当其他线程正在修改它时,没有其他线程可以遍历列表!一次只能在线程上“处理”列表。

    我猜你有以下情况:

    //在列表中添加一些项目的代码

    synchronized(mList){
        mList.add(1, drawableElem);
        ...
    }
    

    //迭代你的代码列表(你的代码简化)

    synchronized (mList) {
        if (mList.size() != 0) {
            int s = mList.size() - 1;
            for (int i = s; i > 0; i -= OFFSET) {
                mList.get(i).doDraw(canv);
            }
            getHead().drawHead(canv);
        }
    }
    

    个别代码看起来不错。他们缝线安全。但是 2 段单独的线程安全代码在更高级别上可能不是线程安全的! 只是您会执行以下操作:

    Vector v = new Vector();
    if(v.length() == 0){    v.length() itself is thread safe!
      v.add("elem");        v.add() itself is also thread safe individually!
    }
    

    但复合运算不是!

    问候, 提比略

    【讨论】:

    • 我知道 Vector 的示例不是线程安全的,因为您没有在 check-then-modify 操作上进行同步,但是在 mList 上同步的两个块是完全线程安全的。
    • 是的..你是对的。我只是想了一遍,你是对的。我最初考虑过 check-then-modify 问题,但对列表的访问受到同步块的保护。正如这里所说,如果使用的是同一个锁,那么一切都应该没问题。干杯,提比留
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    相关资源
    最近更新 更多