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

Android 处理程序发布的Runnable看到错误的对象状态(竞争条件?),android,concurrency,message-loop,Android,Concurrency,Message Loop,我在这里遇到了一个非常奇怪的问题,我在使用Handler.postDelayed发布为可运行的方法中看到了错误的对象状态。我用它来安排2D绘图的绘图调用,这个绘图代码检查某些状态字段(如int和boolean) 现在,这些状态字段可能会在我计划绘制之后发生更改,但是由于所有方法,甚至延迟的调用都在同一线程上执行(对吗?),因此不应该因为共享状态而出现可见性问题 尽管如此,我有时会在计划的绘图中看到一个标志,例如,false,即使它不可能是,因为我在计划绘图之前将其设置为true,并且不再触摸它。

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

现在,这些状态字段可能会在我计划绘制之后发生更改,但是由于所有方法,甚至延迟的调用都在同一线程上执行(对吗?),因此不应该因为共享状态而出现可见性问题

尽管如此,我有时会在计划的绘图中看到一个标志,例如,
false
,即使它不可能是,因为我在计划绘图之前将其设置为
true
,并且不再触摸它。一些伪示例代码:

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

runnable = new Runnable() {
    public void run() {
        // flag is false here
    }
}
这怎么会发生?我不完全确定Android是如何实现这些消息循环的,但我检查了调度draw的方法和调度方法本身中的线程标识,它们都在同一个线程(主UI线程)上调用

这快把我逼疯了,有人能帮忙吗

更新
我注意到问题是由于内部类和外部类分别检查了一次标志。draw代码作为内部类的一部分运行,并在其正确状态下查看标志,而外部类,即使它包含对内部类实例的引用,也始终将标志视为false(错误状态)。我仍然不理解这个问题,但它似乎与类嵌套有关?

我可以在这里看到一些问题

首先,在示例代码中,您将标志声明为scheduleDraw()中的局部作用域变量。我甚至看不出runnable可以访问它

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

private volatile boolean flag;
这样做会告诉Java运行时,这个变量永远不应该被本地线程缓存,所有读写操作都应该直接进入“主内存”

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

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

这里我可以看到一些问题

首先,在示例代码中,您将标志声明为scheduleDraw()中的局部作用域变量。我甚至看不出runnable可以访问它

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

private volatile boolean flag;
这样做会告诉Java运行时,这个变量永远不应该被本地线程缓存,所有读写操作都应该直接进入“主内存”

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

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

我发现了问题:外部类保留了对内部类的引用,其中几个实例可以同时激活(并切换)。由于它们共享外部类的处理程序,因此外部类有时会在处理程序回调上接收来自刚变为非活动的处理程序回调的延迟消息

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


不过非常感谢你的帮助!非常感谢。

我发现了问题:外部类保留了对内部类的引用,其中几个实例可以同时激活(并切换)。由于它们共享外部类的处理程序,因此外部类有时会在处理程序回调上接收来自刚变为非活动的处理程序回调的延迟消息

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


不过非常感谢你的帮助!感谢。

标志只在外部类中定义?是UI线程上的所有代码,还是有其他线程在处理<代码>但我检查了调度绘图的方法和调度方法本身中的线程标识,它们都在同一个线程(主UI线程)上调用。
没有明确说明。您还可以解释一下如何声明标志吗。正如示例中所述,run方法将无法访问它。听起来你有一个范围问题,但如果没有看到基本的类结构,很难说。感谢大家的帮助,我发现了这个问题。查看我的答案。
标志
仅在外部类中定义?是UI线程上的所有代码还是有其他线程在处理<代码>但我检查了调度绘图的方法和调度方法本身中的线程标识,它们都在同一个线程(主UI线程)上调用。没有明确说明。您还可以解释一下如何声明标志吗。正如示例中所述,run方法将无法访问它。听起来你有一个范围问题,但如果没有看到基本的类结构,很难说。感谢大家的帮助,我发现了这个问题。请看我的答案。正如我所说,所有处理程序回调都在同一个线程上运行。Android保证了这一点,我通过检查线程ID进行了双重检查。一定是其他原因。scheduleDraw是如何调用的?这个问题在这一点上不是很清楚,使用AtomicBoolean确实在一定程度上有所帮助,但事实证明它只修复了一个症状(请参阅我的错误答案)。非常感谢!正如我所说,所有处理程序回调都在同一个线程上运行。Android保证了这一点,我通过检查线程ID进行了双重检查。一定是别的原因。你的日程安排怎么样