[英]Simple thread crashes Android app
在Android应用程序中创建启动线程时,我发现了一个非常奇怪的问题。
如果我有以下类线程:
public class TroubleThread extends Thread{
boolean running;
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
@Override
public void run() {
while (isRunning()){
}//end while
}//end run
}
并将其添加到Activity的onCreate(...)方法中的某处,如:
public class MyActivity extends Activity {
TroubleThread myThread;
@Override
public void onCreate(Bundle savedInstanceState) {
//....
myThread = new TroubleThread();
myThread.setRunning(true);
myThread.start();
}
}
该应用程序将崩溃。
但是,如果我将run()方法更改为:
@Override
public void run() {
while (running){ //NOTE THE USE OF DIRECT FIELD ACCESS INSTEAD OF METHOD
}//end while
}//end run
它不会崩溃。
即使我通过使用锁,notify()和wait()解决了我的问题,问题仍然存在:
为什么当使用直接访问该字段时,应用程序继续工作,而在使用它崩溃的方法时?
首先,由于您没有提供MCVE,其他人无法重现您的问题。 这是不幸的,因为这意味着我们无法确定问题的真正原因是什么。 我们只能提出假设。
有些人假设你的问题是由于其他原因造成的; 例如GUI线程上的无限循环。 这似乎是合理的,但没有明确的证据证明这一点。 (我们看不到代码....)
我的假设是这是一个“记忆可见性”问题。 Java语言规范有一章定义了一个线程保证看到另一个线程写入内存的值的情况。 规则相当复杂和技术性,但实质是你需要分析一个线程的内存写入和另一个线程读取的后续内存之间的关系是否发生 。 很多事情会给你这种关系:
final
变量) 但是,对于running
变量,程序中不存在这些内容。 这意味着无法保证在running
变量上循环的线程将看到由另一个线程进行的setRunning
调用的结果:
这三种行为都是可能的......在关系之前没有发生 。
那么为什么一个版本的代码与另一个版本的行为不同呢?
我们无法确定。 实际上,有人需要对您的示例的本机(机器)代码进行深入分析。 它可能与(合法)重新排序有关,优化器在一个案例中而不是另一个案例中完成。 这可能是一个微妙的时间效应。
但不管怎样,JLS表示编译器没有义务确保写入是可见的。 为什么? 因为此代码违反了内存模型的规则 。
解决方案:
在这种情况下,最简单的解决方案是将running
声明为volatile
。
另一种解决方案是将isRunning()
和setRunning
声明为synchronized
方法。
这些中的任何一个都足以提供关系之前的发生 ...并保证isRunning()
看到setRunning()
所做的更新。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.