使用volatile停止线程

时间:2014-01-21 19:54:51

标签: java multithreading volatile

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

代码

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服务。

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

5 个答案:

答案 0 :(得分:8)

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

如果您这样做,则无需更改status标记在其报告角色中的运作方式,您可以在{{}}中删除任何提及的内容。 {1}}。

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

如果sleep不是选项

如果你找到一个很好的理由为什么中断机制对你不起作用,那么我会建议一些比interrupt更复杂的东西,但同样有效且可以说更清洁:保持良好的设计理念让AtomicInteger 报告状态(您可以将其降级为status并将其称为boolean),并创建单独的 {{1对于active控件。

答案 1 :(得分:5)

如果线程仍然是execute() - 它们将不会停止,因为您之后将状态设置为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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

在最终状态中,你使它运行,它可能会导致问题,当你在状态到达之前使它停止时,它可以再次运行。

答案 4 :(得分:1)

使用您当前的设计,您可以在try块的中间以及finally块的中间设置状态值之前检查状态值。

干杯