2013-11-02 8 views
7

фон

Я хотел бы понять, почему фрагмент кода делает не бросить NullPointerException.Почему этот код не выдает исключение 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 экземпляр.

Проблема

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(). На практике я не могу вызвать состояние гонки без введения паузы (т. Е. Разделения нулевой проверки из последующего вызова метода).

Вопрос

Почему первый deliver метод не сгенерирует исключение, когда проверка нулевой и использование объединены в одном заявлении?

+1

Можете ли вы опубликовать выходные данные 'javap', где соответствующие области состояния гонки? – hexafraction

+8

Что происходит, когда вы делаете 'файлы' 'volatile'? – OldCurmudgeon

ответ

5

Две вещи:

  1. Thread.sleep (0) по-прежнему останавливает выполнение (потенциально более 0 миллисекунд). В принципе, даже спящий режим 0 заставляет выполнение в этом потоке останавливаться ненадолго, а затем перезапускать. Это дает другой поток шанс запустить и закончить, поэтому вы можете вызвать состояние гонки.

  2. файлы должны быть volatile, в противном случае JVM разрешено оптимизировать таким образом, чтобы вы никогда не заметили, что он меняет значение, поскольку он не считает необходимым поддерживать согласованность между потоками.

+0

Означает ли это, что JVM может обрабатывать нулевую проверку и последующее использование в качестве атомной операции, если явно не задано 'volatile'? –

+1

В потоке может быть локальная копия переменных, например файлов в вашем случае. Поэтому установка файлов на что-то другое вне этого потока не повлияет на локальную переменную. Поток в конечном итоге проверяет внешние изменения, но в вашем случае, вероятно, в точке, где файлы всегда устанавливаются в значение. Volatile будет гарантировать, что Thread _always_ проверяет внешние изменения. Итак: нет, он не будет рассматривать это как атомную операцию, вам просто повезло в вашем примере на вашем компьютере в вашей JVM. – TwoThe

Смежные вопросы