处理程序发布的Runnable看到错误的对象状态(竞争条件?)

时间:2012-08-09 18:38:35

标签: android concurrency message-loop

我遇到了一个非常奇怪的问题,我在使用Handler.postDelayed发布为Runnables的方法中看到错误的对象状态。我使用它来为2D绘图安排绘制调用,这个绘制代码检查某些状态字段(如整数和布尔值)。

现在可能会发生这些状态字段在我安排绘制之后,但是由于所有方法,甚至延迟调用都在同一个线程上执行(对吗?),因此应该没有可见性由共享状态引起的问题。

不过,我有时会看到一面旗帜,例如false在预定的抽奖中,即使它不可能,因为我在安排抽奖之前将其设置为true并且不再触摸它。一些伪示例代码:

public void scheduleDraw() {
    boolean flag = true;
    handler.postDelayed(runnable);
}

runnable = new Runnable() {
    public void run() {
        // flag is false here
    }
}

这怎么可能发生?我不完全确定Android如何实现这些消息循环,但我在调度绘制和调度方法本身的方法中检查了线程标识,并且它们都在同一个线程(主UI线程)上调用。

这让我发疯,有人可以帮忙吗?

更新 我注意到问题是由于内部类检查了一次标志,而外​​部类检查过一次。绘制代码作为内部类的一部分运行,并且看到标志处于正确的状态,而外部类,即使它包含对内部类的实例的引用,也始终将标志视为false(不正确的状态)。我仍然不明白这个问题,但它似乎与类嵌套有关?

2 个答案:

答案 0 :(得分:1)

我可以在这里看到几个问题。

首先,在您的示例代码中,您将flag声明为scheduleDraw()中的本地范围变量。我甚至无法看到runnable如何访问它。

假设这只是一个拼写错误而且该标志是一个类变量...简单地将布尔值设置为true并不意味着所有线程都会立即看到相同的值。在Java中,一些变量写入可以在线程本地缓存,这意味着其他线程实际上会看到一个不一致的值。避免这种情况的一种方法是声明变量volatile。例如:

private volatile boolean flag;

这样做告诉Java运行时这个变量永远不应该在线程本地缓存,并且所有的读写都应该直接进入“主内存”。

另一个解决方案是使用java.util.concurrent.atomic包中的AtomicBoolean实例

private AtomicBoolean flag = new AtomicBoolean();
...
flag.set(true);

答案 1 :(得分:0)

我发现了这个问题:外部类保留了对内部类的引用,其中几个实例可以立即激活(并切换)。由于它们共享外部类的处理程序,外部类有时会从处于非活动状态的处理程序回调中接收延迟消息。

我现在不再在外层和内层之间共享任何变量。

非常感谢你的帮助!赞赏。