0

我是并发编程的初学者,我想确切地理解为什么当我sleep(1)get() 我的第一个想法中评论时这个程序没有结束,是 sleep(1) 将手交还给Main线程,也许忙等待有有什么关系?

public class Rdv<V> {
  private V value;

  public void set(V value) {
    Objects.requireNonNull(value);
    this.value = value;
  }

  public V get() throws InterruptedException {
    while(value == null) {
        Thread.sleep(1);  // then comment this line !
    }
    return value;
  }

  public static void main(String[] args) throws InterruptedException {
    Rdv<String> rendezVous = new Rdv<>();
    new Thread(() -> {
      try {
        Thread.sleep(5000);
      } catch (InterruptedException e) {
        throw new AssertionError(e);
      }
      rendezVous.set("hello");
    }).start();

    System.out.println(rendezVous.get());
  }
}
4

1 回答 1

2

首先,这个程序被严重破坏。无法保证即使在睡眠状态下它也会终止。它可能会,但可能不会。

问题是值字段不是易失性的,并且在获取/设置值时不使用同步或其他锁。这意味着不能保证一个线程会看到另一个线程的变化!应该停止程序的写入值对于等待的线程可能永远不可见。睡眠到位后,一切正常,因为 Java 在解释模式下运行。没有睡眠,即时编译器就有时间启动和优化代码。它看到 while 循环可以重写为执行相同操作的更高效的版本:永远循环。这就是它的作用。

最简单的解决方法是将值字段声明为 volatile。然后 Java 知道它可能会改变并避免优化读取。

短版,除非您非常了解 Java 内存模型,否则始终同步访问线程之间共享的数据。安全总比后悔好!

于 2017-11-19T19:56:58.497 回答