7

バックグラウンド

コードのスニペットがNullPointerException をスローしない理由を理解したいと思います。

ソースコード

次のコードを検討してください。

public class Agent {
  public List files = new ArrayList();

  public void deliver() {
    if( files != null && files.iterator().hasNext() ) {
      File file = (File)files.iterator().next();
    }

    files = new ArrayList();
  }
}

deliverメソッドは繰り返し呼び出されますが、次のコードは別のスレッドで実行されます。

  public void run() {
    agent.files = null;
  }

agentインスタンスは 1 つだけです。

問題

NullPointerException がスローされることはありません。

ただし、deliverメソッドが一時停止すると、0 ミリ秒であっても、予想どおり NullPointerException がスローされます。

  public void deliver() {
    if( files != null ) {
      Thread.currentThread().sleep( 0 );

      if( files.iterator().hasNext() ) {
        File file = (File)files.iterator().next();
      }
    }

    files = new ArrayList();
  }

私の理解では、理論的には、のチェックfiles == nullと呼び出しの間に競合状態があるということfiles.iterator().hasNext()でした。実際には、一時停止を導入せずに競合状態をトリガーすることはできません (つまり、後続のメソッド呼び出しから null チェックを分割します)。

質問

delivernull チェックと使用法が同じステートメントで組み合わされている場合、最初のメソッドが例外をスローしないのはなぜですか?

4

1 に答える 1

5

2つのこと:

  1. Thread.sleep(0) は引き続き実行を停止します (0 ミリ秒を超える可能性があります)。基本的に、スリープが 0 であっても、そのスレッドでの実行は一時的に停止し、その後再開します。これにより、他のスレッドが実行されて終了する機会が与えられます。これが、競合状態をトリガーできる理由です。

  2. volatileそうしないと、JVM は、スレッド間の一貫性を維持する必要があるとは考えないため、値が変化していることに気付かないような方法で最適化することが許可されます。

于 2013-11-02T00:21:46.007 に答える