【问题标题】:Thread doesn't stop [duplicate]线程不会停止[重复]
【发布时间】:2021-08-13 10:43:47
【问题描述】:

我有以下课程:

public class ThreadStopTest {

private static boolean enabled = true;

private static int milliseconds = 5;

public static void main(String[] args) {
    new Thread(ThreadStopTest::process).start();
    sleepMillSeconds(milliseconds);
    disableProcessing();
}

private static void disableProcessing() {
    enabled = false;
    System.err.println("Processing disable called");
}

private static boolean isEnabled() {
    return enabled;
}

private static void process()  {
    boolean printed = false;
    while (isEnabled()) {
        if(!printed) {
            System.err.println("Processing started");
            printed = true;
        }
    }
    System.err.println("Processing finished");
}

private static void sleepMillSeconds(int mills) {
    try {
        Thread.sleep(mills);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

如果毫秒> 5,则此类执行挂起并且永不停止, 但如果毫秒

在 enabled 参数中添加 volatile 修饰符可以解决问题,但我想知道这种奇怪行为的原因是什么。有人有想法吗?

【问题讨论】:

  • 如果您有一个长时间运行或阻塞的方法,有时使用标准的 java 方法来停止执行可能是合理的 - 声明该方法抛出 InterruptedException 并检查 Thread.currentThread().isInterrupted( ) 在你的循环中抛出一个 InterruptedException,而不是检查像你的 isEnabled 这样的合成值。

标签: java multithreading


【解决方案1】:
private static boolean enabled = true;

运行process() 方法的线程没有理由相信这个值会被另一个线程改变,所以 JVM 可能决定它不会费心多次读取它的值(它可以 多读一遍,只是不保证)。

将其设为volatile,表示它需要在每次评估isEnabled() 时读取当前值:

private static volatile boolean enabled = true;

更正式地说,volatiledisableProcessing() 的写入和isEnabled() 的读取之间创建一个happens-before relationship

从上面的定义可以得出:

  • ...
  • 对 volatile 字段(第 8.3.1.4 节)的写入发生在对该字段的每次后续读取之前。
  • ...

睡眠时间是否超过 5 毫秒有什么关系?谁知道呢,这可能是由于许多特定于 JVM 的原因。这可能是因为Thread.sleep 方法的工作方式(强调我的):

使当前执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统计时器和调度程序的精度和准确性。

如果您休眠 5 毫秒或更短时间,则可能是它实际上根本没有休眠 ,或者休眠时间比线程调度时间短,因此您更新了enabled 在另一个线程中第一次读取之前的标志。

【讨论】:

  • 感谢您对 volatile 关键字的解释。我想知道这个特定的 JVM 原因,线程睡眠会影响什么,以及如果线程不睡眠,为什么它在没有 volatile 的情况下工作。
  • 不值得尝试找出睡眠延迟似乎使它起作用的具体原因:除非您在写入和读取之间创建发生前的关系,否则您的代码无法保证正确运行.
  • 原因是 JIT 编译器优化。您的流程方法多次调用 isEnabled() 方法。这就是为什么 JIT 编译器可以用一些常数来代替它。尝试使用 -XX:-Inline 或 -XX:CompileCommand=exclude, ThreadStopTest .isEnabled VM 选项运行您的测试。这应该关闭 isEnabled 方法的 JIT 优化。
猜你喜欢
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多