【问题标题】:Stop thread using volatile使用 volatile 停止线程
【发布时间】:2014-02-11 14:45:58
【问题描述】:

我遇到了停止我正在创建的一些线程的问题。

代码

private volatile int status = STOPPED;

@Override
public void run() {

logger.info("Thread with ID : " + id);
status = RUNNING;
while (status == RUNNING) {                
    try {
        execute();      //<-- very intensive 
        status = IDLE;
        Thread.sleep(DYNAMIC_VALUE);
    } catch (Exception e) {
        logger.info("An exception occured in thread with ID : " + id);
        e.printStackTrace();
    } finally {
        if(status == IDLE)
            status = RUNNING;
    }
}

    logger.info("Thread with ID : " + id + " just exited.");
}

@Override
public void stop() {
    status = STOPPED;
    logger.info("Thread with ID: " + id + " is stopped.");
}

有时当我调用stop() 时,并非所有线程都停止了。 这从来没有被称为

logger.info("Thread with ID : " + id + " just exited.");

相反,execute() 内的日志消息重复出现,因此它没有得到 一路上卡在某个地方。就像status 的值没有改变一样。

execute() 方法是重量级的。它做了很多事情,比如更新 数据库和调用 Web 服务。

你有没有发现我停止线程的方式有什么问题,或者我应该看看 更深?我是否涵盖了这方面?

【问题讨论】:

    标签: java multithreading volatile


    【解决方案1】:

    如果在设置 status=STOPPED 时 execute() 正在线程中运行,则线程将在从 execute() 返回时设置 status=IDLE,然后 finally 块将设置 status=RUNNING,并且您的循环循环。

    【讨论】:

      【解决方案2】:

      在finally状态下,你让它RUNNING,可能会导致问题,当你在它到达while条件之前让它STOPPED,它可以再次RUNNING。

      【讨论】:

        【解决方案3】:

        如果线程仍然是execute()-ing,它们将不会停止,因为您随后将它们的状态设置为 IDLE,忽略之前的值。即使在status = IDLE 之前添加支票也并非在所有情况下都有效。

        我建议使用 AtomicInteger 而不是 volatile int 并使用其 compareAndSet 方法,即:

            while (status.get() == RUNNING) {                
                try {
                    execute();      //<-- very intensive 
                    if (!status.compareAndSet(RUNNING, IDLE))
                        break;
                } catch (Exception e) {
                    logger.info("An exception occured in thread with ID : " + id);
                    e.printStackTrace();
                } finally {
                    try {
                        Thread.sleep(DYNAMIC_VALUE);
                    } catch (InterruptedException e) {}
                    if (!status.compareAndSet(IDLE, RUNNING))
                        break;
                }
            }
        

        您还应该在将其状态设置为 STOPPED 后中断线程以中断sleep(如果适用)。

        【讨论】:

        • +1 您也可以将status 设为enum 并使用AtomicReference。这不会改变线程问题,只会使status 更加类型安全和Java 惯用语。 :)
        • 该死的,我错过了。谢谢你提供的比我要求的多。赞成并接受。
        【解决方案4】:

        我建议现在使用Thread#interrupt() 而不是status 标志的双重角色(既控制 线程和报告 状态)。这与您的Thread.sleep() 调用完美匹配,它会自动抛出InterruptedException

        如果您这样做,您无需更改您的 status 标志在其报告角色中的作用方式 - 您可以删除任何在 stop() 中提及的内容.

        这种方法的一个好处是您还可以在execute 后面的代码中到处添加一个快速的Thread#interrupted 检查,从而使您的线程更快地停止;或者让您在转到sleep 之前一次做更多的工作。完全消除sleep 将成为另一个有趣的选择。

        如果interrupt 不是一个选项

        如果你找到了中断机制对你不起作用的充分理由,那么我会建议比AtomicInteger 更复杂的东西,但同样有效且可以说更干净:保持良好的设计理念让@987654333 @report 状态(您可以将其降级为 boolean 并将其命名为 active),并为 running 控件创建一个独立 boolean

        【讨论】:

        • 我考虑过使用interrupt(),但是如果线程卡住等待Web服务响应怎么办。我不希望它立即退出,我想先完成整个循环然后退出。你的意见?哦,当然赞成。感谢您的帮助。
        • 您必须检查您的 I/O 是否可中断——例如,java.io 不是,而对于 Java NIO,我认为实际上可以选择两者。如果您发现它可以中断,那么您将不得不继续使用当前的手动模式。但是,在这种情况下,我会建议一些不太复杂但同样有效的方法:保持良好的设计理念让status report 状态(您可以将其降级为 boolean 并调用它active),并为 running 控件创建一个单独的 boolean
        • 喜欢设计建议。我会跟着他们。再次感谢。
        【解决方案5】:

        使用您当前的设计,您可以在将 status 设置在 try 块的中间和 finally 块的中间之前检查它的值。

        干杯

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多