如何正确使用synchronized?

时间:2011-08-29 12:53:00

标签: java android synchronized synchronized-block

这段代码:

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:

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

编辑3:

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

4 个答案:

答案 0 :(得分:3)

你正在做什么看起来正确......但这就是全部:

  1. 与您同步的对象无关,它不必是列表本身。
  2. 当访问共享资源时,如果所有线程始终在同一对象上同步,那么重要的是什么。
  3. 任何对SWING(或其他图形库)的访问都必须在AWT-Thread中进行。
  4. 进行编辑:

      

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

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

    再次:任何对共享资源的访问都必须以某种方式受到保护。您好像在这里使用synchronized而不是在清空列表的位置。

答案 1 :(得分:0)

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

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

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

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

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

答案 2 :(得分: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();
   }
 }

对您有益的是,您无需担心同步mListBlockingQueue提供了10种特殊方法。您可以在doc中查看它。很少来自javadoc:

  

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

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

答案 3 :(得分:-1)

您正在获取索引超出边界异常,因为有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!
}

但复合操作不是!

此致 Tiberiu