【问题标题】:Is this starvation?这是饿死吗?
【发布时间】:2025-12-01 15:05:03
【问题描述】:

我发现了一段代码,其中线程似乎饿死了。下面是一个简化的例子。这是饥饿的例子吗?线程不终止的原因是什么?

注意:将睡眠更改为 1 有时会导致终止。注释掉的 Thread.yield() 将解决问题(对我来说)。

public class Foo {

    public static boolean finished = false;

    public static void main(String[] args) {

          Runnable worker = new Runnable() {

                 @Override
                 public void run() {

                      try {
                           Thread.sleep(10);
                      } catch (InterruptedException e) {
                          // TODO Auto-generated catch block
                          e.printStackTrace();
                      }

                      finished = true;
                 }
            };

            new Thread(worker).start();

            while (!finished) {
//          Thread.yield();
        }
    }
}

【问题讨论】:

  • 尝试将finished 声明为volatile

标签: java multithreading


【解决方案1】:

您可能需要了解 Java 内存模型。多线程不仅仅是交错线程的动作;它是关于一个线程对另一个线程的操作的可见性

这个问题的根本在于面对并发性时需要积极优化:确保线程之间内存一致性的任何机制都是昂贵的,并且很多(大部分)数据没有在线程之间共享。因此,未明确标记volatile 或受锁保护的数据默认被视为thread-local(当然没有严格的保证)。

在您的情况下,finished 就是这样一个变量,如果它取悦运行时,则可以将其视为线程局部变量。它确实取悦它,因为

while (!finished);

循环可以改写成只是

if (!finished) while (true);

如果你在循环中做了任何重要的工作,它会执行得更好一些,因为finished 的读取不会被不必要地重复,因此可能会破坏整个 CPU 缓存行。

上面的讨论应该足以回答你的直接问题,“这是饥饿吗”:循环没有完成的原因不是饥饿,而是看不到其他线程的写入。

【讨论】:

  • 感谢您的有用回答!真正让我感到困惑的是,线程几乎总是在它只包含一些廉价语句时终止(类似于 Thread.sleep(1))。当我添加由上面的 Thread.sleep(100) 模拟的进一步代码时,问题出现了...
  • 如果您仍然想知道为什么会这样,原因是main 中的无限循环需要一些时间才能被 JIT 编译成包含“循环提升”优化的优化版本.只有在该优化版本第一次运行后,代码才会进入无限循环,不再检查标志。
【解决方案2】:

这里没有饥饿,因为你没有做任何工作。 Starvation 表示各种线程正在尝试访问相同的有限资源集。每个线程在这里尝试访问的资源是什么?他们没有“吃”任何东西,所以他们不会饿死。

【讨论】: