2013-05-02 1 views
2

При просмотре this question я заметил этот код:Почему этот код не работает?

class MyThread extends Thread { 
    private boolean stop = false; 

    public void run() { 
    while(!stop) { 
     doSomeWork(); 
    } 
    } 

    public void setStop() { 
    this.stop = true; 
    } 
} 

Однако я не понимаю, почему бы это потерпеть неудачу. Разве другие потоки не получают доступ к «фактической» переменной остановки?

+0

Как вы вызываете 'setStop()'? из того же экземпляра MyThread или другого? –

+0

Я не знаю, к сожалению, это абстрактный пример из вопроса, связанного с –

+0

setStop будет вызываться из другого потока. весь метод выполнения должен сделать, чтобы остановить - это просто возврат, ему не нужен флаг. –

ответ

5

Переменная экземпляра stop должна быть изменчивой, в противном случае нет гарантии, что другие потоки будут видеть в ней изменения. На работе много конфликтующих интересов: потоки хотят согласованного представления состояния программы, процессоры хотят иметь возможность кэшировать данные, JVM хочет иметь возможность переупорядочивать инструкции. Создание переменной переменных volatile означает, что она не может быть кэширована и что происходит - до установления отношений, которые ограничивают переупорядочение команд.

См. this other answer (+1), что является хорошим примером того, что может произойти переупорядочение без указания переменной изменчивости.

(. Кстати using interruption for thread cancellation предпочтительнее, чтобы с помощью переменной экземпляра)

+0

Вы должны упомянуть подъем, который является причиной №1, это может привести к сбою. –

+0

@JohnVint "подъем"? – user2246674

+1

@ user2246674 Я создал свой собственный ответ на адрес hoisting. –

0

Другие темы, не гарантируется, чтобы увидеть обновленные значения стоп - нужно установить «происходит до» отношений. Самый простой способ - сделать стоп неустойчивым.

1

Переменная остановка должна быть объявлена ​​изменчивой.

Хотя я предпочитаю использовать прерывание, чтобы остановить поток.

6

JIT-компилятор может изменить порядок чтения и записи в приложении, пока

  1. действия, последовательно последовательны и
  2. измененные действия не нарушают внутри потоков семантику.

Это просто причудливый способ сказать, что все действия должны происходить так же, как если бы они выполнялись только одним потоком. Таким образом, вы можете получить JIT перекомпилировать код, чтобы посмотреть, как этот

class MyThread extends Thread { 
    private boolean stop = false; 

    public void run() { 
    if(!stop){ 
     while(true){ 

     } 
    } 
    } 

Это правовая оптимизация называется грузоподъемных. Он по-прежнему действует так же, как и серийный, но предлагает неожиданные результаты при использовании нескольких потоков.

Объявив поле volatile, вы говорите Java, чтобы он не выполнял никаких заказов. Наряду с консистенцией памяти, как упомянуто Nathan Hughes

+0

Будет ли JVM проверять, действительно ли 'doSomeWork()' устанавливает поле 'stop' перед подъемом? Я предполагаю, что да, поскольку в противном случае оптимизация кажется не такой оптимальной :) – dlev

+0

@dlev Да, абсолютно! В противном случае это приведет к ужасной оптимизации. –

+1

Отличная информация, спасибо! –