java - 简单线程使 Android 应用程序崩溃

标签 java android multithreading

我在 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 语言规范有一章定义了一个线程在什么情况下保证看到另一个线程写入内存的值。规则比较复杂,技术性强,其实质就是要分析一个线程写内存和另一个线程读内存之间是否存在happens before关系。许多事情会给你这种关系:

  • 线程写/读一个共享的volatile变量
  • 线程使用同一个锁进行同步
  • 线程开始
  • 线程连接
  • 完成构造函数(用于final 变量)

但是,关于 running 变量,这些东西都不存在于您的程序中。这意味着无法保证running 变量上循环的线程会看到另一个调用setRunning 的结果主题:

  • 它可能会立即看到变化
  • 稍后可能会看到更改
  • 它可能永远看不到它们

所有这三种行为都是可能的......在没有先于关系的情况下。

那么为什么一个版本的代码与另一个版本的行为不同?

我们不能确定。实际上,有人需要对您的示例的 native (机器)代码进行深入分析。这可能与优化器在一种情况下而不是另一种情况下完成的(合法)重新排序有关。这可能是一个微妙的时间效应。

但无论哪种方式,JLS 都表示编译器没有义务确保写入可见。为什么?因为这段代码违反了内存模型的规则

解决方案:

在这种情况下,最简单的解决方案是将running 声明为volatile

另一种解决方案是将isRunning()setRunning 声明为同步 方法。

其中任何一个都足以提供先于 关系...并保证isRunning() 看到setRunning().

关于java - 简单线程使 Android 应用程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36150385/

相关文章:

java - 缓存谷歌地图v2标记位图

java - LayoutInflater 不更新 TextView

java - setOnItemClickListener() 不适用于 Fragment 中的 ExpandableHeightGridView

android - AOSP : where can I find the layout of the new clock (fonts and size)?

c# - 在共享主机中在 IIS/WCF 下触发线程/进程的最佳方法

java - 使用hibernate懒加载主要有哪些问题?

java - 从 Hibernate 3 迁移到 Hibernate4 不断获取不支持的嵌套事务

java - 使用 Spring Security 取消 Spring 流的安全

java - 如何引用 Thread 类的新实例?

c++ - 计时器是否从另一个线程启动?