【问题标题】:Java Thread not starting (or taking a long time to start)Java 线程未启动(或需要很长时间才能启动)
【发布时间】:2017-03-20 07:16:30
【问题描述】:

我有一个简单的(或者至少我认为是)Java 应用程序,它在线程中执行一些工作。

类似:

public class Task implements Runnable {

    public boolean running = false;

    public void run() {
        running = true;
        // long running processing
    }
}

...

void startTask() {
    Task task = new Task();
    Thread thread = new Thread(task);
    thread.start();
    // I added this thinking the calling thread might terminate before
    // the new thread starts
    while (!task.running) {
        try {
            Thread.sleep(1);
        } catch (InterruptedException ie) {
            // log error
    }
}

startTask() 上面的调用是为了响应 REST 请求(这是一个 Spring Boot 应用程序)。它在我的开发机器(Windows 10,Oracle JDK)和 Amazon EC2 实例(Amazon Linux,OpenJDK)上运行良好,但在 Google Compute 实例(Ubuntu 16.04,OpenJDK)上运行良好。在后一种情况下,工作线程要么永远不会启动(task.running 永远不会设置为 true),要么它有时会在 60+ 秒后启动。我很困惑。

鉴于任务本身并不是很复杂(加上设置“运行”标志是它所做的第一件事,而且这从未发生过)让我认为这是一些奇怪的 JVM/系统相关问题,但是我真的不知道。

最令人沮丧的是它有时可以工作(通常是我在重建后第一次上传/运行它时)。并且从来没有在我的电脑上工作失败过。

编辑:我曾尝试在 Ubuntu 中使用 Oracle JRE,但同样没有成功。

第二次编辑:是的,我在这里编写示例代码时犯了一些错误。固定。

【问题讨论】:

    标签: java multithreading spring-boot


    【解决方案1】:

    Runnable 是一个接口,因此您正在创建一个名为 Task 的新接口!但是,您还提供了 run() 方法的实现。这不会编译。

    可能,这就是你想要做的:

    class Task implements Runnable {
        public boolean running = false;
    
        @Override
        public void run() {
            running = true;
            // long running processing
        }
    }
    

    另一个错误是你直接调用了线程的run()方法! 您应该改为调用 start()。

    示例代码可能如下所示:

    void startTask() {
        Task task = new Task();
        Thread thread = new Thread(task);
        thread.start();
        // I added this thinking the calling thread might terminate before
        // the new thread starts
        while (!task.running) {
            try {
                Thread.sleep(1);
            } 
            catch (InterruptedException ie) {
                // log error
            }
        }
    } 
    

    【讨论】:

    • 是的,谢谢,我把示例代码塞满了。实际代码如您所建议。我已经更新了样本。谢谢。
    • 没问题,希望对您有所帮助。
    【解决方案2】:

    这不是您应该在 Java 中启动线程任务的方式。在这种情况下,您只需调用 run 方法。要运行线程,您应该使用 start 方法:

     thread.start();
    

    【讨论】:

    • 你是对的。我在示例代码中犯了一个错误 - 我已经编辑了问题,以便它现在与我的实际代码相匹配(它应该调用 thread.start()
    【解决方案3】:

    您的代码不是线程安全的,因为您从多个线程访问单个变量,因为您从未使用任何安全访问结构来访问此变量,这意味着内部 java 代码优化器可能会将您的 while 循环优化为 @ 987654323@循环。

    虽然您可以声明变量volatile 来解决问题,但真正的解决方案是使用Object.waitObject.notifyAll 来处理等待时间。

    在你的监听器启动主线程后,它应该进入一个同步块内的 while 循环,检查条件之间的等待,例如:

    thread.start();
    // I added this thinking the calling thread might terminate before
    // the new thread starts
    try {
        synchronized (task) {
            while (!task.running) {
                task.wait();
            }
        }
    } catch (InterruptedException ie) {
        // log error
    }
    

    然后在你的任务里面,你需要设置running为true,然后通知所有线程。

    synchronized (this) {
        this.running = true;
        this.notifyAll();
    }
    

    【讨论】:

      【解决方案4】:

      您还需要将您的running 变量标记为volatile,如下所示,否则将无法保证其他线程看到running 变量的更改。

      private volatile boolean running;
      

      对 volatile 变量的更改始终对其他线程可见

      你可以看here更多关于volatile的信息

      【讨论】:

        【解决方案5】:

        我认为这是一个内存模型/同步问题。您有一个线程写入running 标志,而另一个线程读取它没有进行任何同步。这可能导致读取(在本例中为主)线程看不到由写入(在本例中为子)线程对running 所做的更新。

        解决方案:

        1. 使用同步方法读取和更新running 标志。
        2. running 声明为volatile

        最令人沮丧的是它有时会起作用(通常是我在重建后第一次上传/运行它时)。并且从来没有在我的电脑上工作失败过。

        众所周知,涉及不正确同步的错误很难追踪。问题的根源在于,在现代多核处理器上,您的代码是否有效……取决于是否/何时刷新内存缓存。这可能取决于您拥有多少物理内核、系统的繁忙程度、(操作系统提供的)线程调度的行为。当您使用调试器或添加跟踪打印时,有责任改变您试图追踪的行为。

        最佳策略如下:

        1. 尽可能使用 java.util.concurrent.* 类提供的更高级别的并发功能。

        2. 避免使用裸线程、共享变量、互斥锁等。 (它们很难正确使用。)

        3. 如果/当你确实需要使用低级原语时:

          • 确保您真正了解 Java 内存模型,并且
          • 非常仔细地分析您的代码,以确保它没有经典的竞争条件和内存访问危险。

        【讨论】:

          猜你喜欢
          • 2013-11-15
          • 2014-02-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多