2016-10-17 4 views
3

Ответ на эту программу должен быть «Замедление» через 5 секунд, но я получаю «Смена сделанного» и «Совершено». У меня нет метода getDone как синхронизированный. Любые идеи, что я делаю для потока, чтобы закончить обработку.java synchronized Очистка проводов

public class Main { 
    private static boolean done = false; 
    private static int count; 

    public static void main(String[] args)throws InterruptedException { 
     new Thread(() -> { 
      while (!getDone()) { 
       count = count + 1; 
      } 
      System.out.println("DONE!!!!"); 
     }).start(); 
      Thread.sleep(5000); 
     System.out.println("Changing done"); 
     synchronized (Main.class) { 
      done = true; 
     } 
    } 

    public static boolean getDone() { 
     return done; 
    } 
} 
+2

Ваша программа делает именно то, что я ожидаю от нее. Вы ожидали, что ваш анонимный «Runnable» не закончит? – CodeBlind

+0

@CodeBlind: доступ к 'done' не синхронизирован должным образом, вызов' getDone() 'может не увидеть текущее значение. – user140547

+1

Худшее, что может случиться здесь, - это рабочий поток, возможно, не видит изменения в 'done' немедленно, но это, безусловно, в конечном итоге. Поскольку только один поток когда-либо изменяет 'done', вы можете избежать использования ключевого слова' volatile' в своем объявлении и полностью уничтожить блок 'synchronized'. Но это все вода под мостом - я до сих пор не понимаю, что делает OP. Кажется, он удивлен, что рабочий поток печатает 'DONE !!!!'. – CodeBlind

ответ

1

У меня нет метода getDone как синхронизированный. Любые идеи, что я делаю для потока, чтобы закончить обработку.

Как вы отметили, нет явной синхронизации памяти между done, как видно из вращающейся нити и основного потока. Хотя основной поток пересекает барьер памяти записи при выходе из блока synchronized, нет явного барьера памяти чтения, пересеченного прядильной нитью.

Однако есть много способов, по которым поток может видеть обновленную информацию. Если операционная система меняет поток из работающего процессора, кэшированная память может быть потеряна, поэтому, когда поток будет заменен обратно, он запросит done из центральной памяти и увидит обновление.

Кроме того, хотя ваш пример кода не показывает, если звонить в другие synchronized метод (такие как System.out.println()) или пересекать другие барьеры памяти (доступ к другим volatile полю), то это также приведет к done обновить ,

5

Если вы не синхронизации доступа к done должным образом, это означает, что ваш код может потерпеть неудачу, то есть нить может не увидеть обновленное значение.

Это не означает, что гарантированное значение не будет видно, если синхронизация не будет выполнена правильно. Таким образом, во многих случаях запись до done по-прежнему остается видимой (факт, что сломанный код все еще работает во многих случаях, затрудняет параллельное программирование). В каждом случае это не гарантируется.

+0

Выполнение изменения выполняется в синхронизированном блоке. Это будет видно. Это указано в [JLS] (https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4) под моделью памяти. Есть ли у вас конкретная ссылка, чтобы продемонстрировать, почему поток может не увидеть обновленное значение? Или, может быть, это в ссылке, которую я включил, но после беглого чтения я думаю, что сделанные изменения будут видны. – matt

+0

@matt: не только запись должна быть синхронизирована, но и прочитана. Для этого вам не нужны JLS. см., например, http://stackoverflow.com/questions/9196723/learning-java-use-of-synchronized-keyword quote из принятого ответа (выделение мое): когда синхронизированный метод завершается, он автоматически устанавливает случившееся раньше отношение с ** любым последующим вызовом синхронизированного метода для того же объекта ** – user140547

+0

Я не говорю, что это не будет условие гонки. Синхронизированный гарантирует, что изменения будут видны в потоках. Это похоже на объявление переменной volatile. – matt

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