Java 使用volatile停止线程

Java 使用volatile停止线程,java,multithreading,volatile,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 intensiv

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

代码

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.");
}
相反,
execute()
中的日志消息会重复出现,因此不会被删除 在路上的某个地方卡住了。这就像
status
的值不会改变一样

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

你认为我停止线程的方式有什么问题吗?或者我应该看看吗
更深的?我是否介绍了这方面的内容?

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

在finally状态下,如果使其运行,则会导致问题,当您在while条件出现之前使其停止时,它可以再次运行。

如果线程仍然处于
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;
        }
    }
while(status.get()==正在运行){
试一试{

execute();//我建议使用
Thread#interrupt()
而不是现在为
status
标记使用双重角色(控制线程和报告状态)。这与
Thread.sleep()调用非常匹配,它将自动抛出
InterruptedException

如果您这样做,您不必更改您的
状态
标志在其报告角色中的运行方式,您可以在
stop()
中删除对它的任何提及

这种方法的一个好处是,您还可以在
执行
后的代码中添加一个快速的
线程#中断的
检查,从而使线程停止得更快;或者允许您在进入
睡眠之前一次做更多的工作这将成为另一个有趣的选择

如果
中断
不是一个选项

如果您找到了中断机制不适用于您的一个很好的理由,那么我建议使用比
原子整数
更不复杂的方法,但同样有效且可以说更干净:保留好的设计思想,让
状态
报告状态(您可以将其降级为“代码> BooLoe<代码>,并调用它>代码> Active < /COD>),并为<<代码>运行< < /Cord>控件>创建一个单独的<代码>布尔< /代码> .< /p> 使用当前的设计,在将其设置在尝试块的中间以及最后块的中间之前,可以检查状态值。


干杯

+1您还可以将
状态
设置为
枚举
并使用
原子引用
。这不会改变线程问题,只会使
状态
更加类型安全和Java惯用。:)该死,我错过了。感谢您提供了比我要求的更多。投票通过并接受了。我考虑过使用中断(),但是如果一个线程在等待web服务响应时被卡住了该怎么办呢?我不希望它立即退出,我希望先完成整个循环,然后退出。你的想法?哦,当然,我投了赞成票。谢谢你的帮助。你必须检查你的I/O是否是可中断的——例如,
java.io
不是,而java NIO我认为不是实际上,这里有两种选择。如果您发现它是可中断的,那么您将不得不继续使用当前的手动模式。但是,在这种情况下,我会建议一些不太复杂但同样有效的方法:保留好的设计思想,让
状态
报告状态(您可以将其降级为布尔值,并称之为活动的),然后为运行的控件创建一个单独的布尔值。我喜欢这些设计建议。我会遵循它们。再次感谢。
    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;
        }
    }