2

这段代码:

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 = Array Index Out Of Bounds Exception 代码不完整,减少到需要的部分。但是为了让你开心,OFFSET 是 4,想象一下有一个 for 循环在开头添加了一点数据。第二个线程读取和/或修改列表。

编辑2:

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

编辑3:

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

4

4 回答 4

2

你在做什么看起来不错......但就是这样:

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

对您的编辑:

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

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

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

于 2011-08-29T13:08:04.073 回答
0

我的建议是使用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();
   }
 }

对您有益的是,您不必担心同步您的mList. BlockingQueue提供 10 种特殊方法。您可以在文档中检查它。来自javadoc的很少:

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

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

于 2011-08-29T14:36:37.890 回答
0

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

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

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

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

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

于 2011-08-29T14:02:56.770 回答
-1

你得到 Index out of Bounds Exception 因为有 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!
}

但是复合操作不是!

问候, 提比留

于 2011-08-29T13:37:59.400 回答