我有一个可运行的实例,它在其 run
方法结束时再次 self 调度:
private class MyRunnable implements Runnable {
private volatile boolean cancelled = false;
private Handler handler;
public MyRunnable(Handler h){
handler = h;
}
@Override
public void run(){
//Do stuff
if(!cancelled){
//Preemtion possible here?
handler.postDelayed(this, 1000);
}
}
public void selfStart(){
cancelled = false;
handler.removeCallbacks(this);
handler.post(this);
}
public void selfCancel(){
cancelled = true;
handler.removeCallbacks(this);
}
}
可运行对象首先在主线程中调度,从 Activity 的 onStart
调用 selfStart
。
同时,可以从 Activity 的 onStop
以及广播接收器外部取消可运行对象(调用 selfCancel
)。
AFAIK Runnable.run
、Activity.onStop
和 BroadcastReceiver.onReceive
在同一个线程(主线程)中运行,所以一开始乍一看我以为不会有线程安全问题。
但有时,可运行对象在其 run
调用过程中被抢占,然后从 Activity 或接收器中取消,然后再次恢复并重新调度自身。
这可能吗?
更新:
我会尽力更好地解释这个问题。上面显示的类旨在在主线程中定期运行任务。在“do stuff”注释中,实际上有一些代码使用传递给 MyRunnable
构造函数的值来更新 TextView
。当收到某些 Intent 时,该 Activity 会取消当前的可运行状态并启动一个新的可运行状态。尽管当前的可运行对象总是被要求在创建新的可运行对象之前取消自身,但有时它会与新的可运行对象一起运行,因此 TextView 会显示交替值。这不是预期的行为。
我认为如果可运行对象当前正在主线程中运行,它将运行直到完成,然后其他可运行对象或事件处理程序将从队列中取出并在需要时执行,但不会有挂起的事件或可运行对象“执行了一半”。
主线程中运行的任务有两种与问题相关:
- R1:
MyRunnable
自调度任务。运行,然后再次 self 发布,延迟 1 秒。 - R2:请求取消当前
MyRunnable
实例并创建新 R1' 的事件处理程序。这些是随机发生的,并且仅执行一次。
我考虑过两种情况。第一个:
- R1 已在主线程中运行。
- R2 到达并在主线程中排队。
- R1 完成运行并再次发布。
- R2 运行并删除 R1 的回调。
- R1 不应再次运行。
第二个:
- R1 未运行,但已安排。
- R2 到达并删除 R1 的回调。
- R1 不应再次运行。
理论上,如果没有抢占,并且只有一个线程,为什么有时主线程中有两个R1?
最佳答案
由于您在 selfStart 或 selfCancel 上没有同步,所以这是完全有可能的。
一个不相关的注释,在 run 方法中的 if 语句检查了 cancelled
的值之后,可以在单独的线程上调用 selfCancel
。然后,MyRunnable
将再获得一次运行调用,该调用将在被取消后立即结束。
关于java - 主线程: can runnables be preempted?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21438939/