Java, видимость и появление бесконечного цикла - PullRequest
3 голосов
/ 07 января 2012

Я изучаю Java-параллелизм на практике , и там объясняется, почему следующий фрагмент кода плох:

public class NoVisibility {
    private static boolean ready;
    private static int number;
    private static class ReaderThread extends Thread {

      public void run() {
      while (!ready) {
        Thread.yield();
        System.out.println(number);
      }
    }

    public static void main(String[] args) {
       new ReaderThread().start();
       number = 42;
       ready = true;
   }
}

Этот код может печатать 0 или цикл навсегда.Хотя легко понять, почему NoVisibility может печатать 0 вместо 42 (из-за проблемы с переупорядочением), я немного запутался в бесконечном цикле.

Какой практический сценарий, где бесконечныйцикл может возникнуть, в этом коде?

Ответы [ 3 ]

8 голосов
/ 07 января 2012

Цикл останавливается, когда ready установлен на true. И ready устанавливается на true основным потоком. Но поскольку поле ready не является энергозависимым, зацикливающийся поток может продолжать видеть кэшированное значение: false.

Ключевое слово volatile гарантирует, что все потоки, читающие изменяемое поле, действительно увидят последнее значение, сохраненное в этом поле, любым другим потоком. Без volatile у вас нет этой гарантии.

0 голосов
/ 29 января 2015

Пожалуйста, смотрите код ниже, он представляет цикл Infinite довольно на x86. Пробовал с jdk8 и jdk7

package com.snippets;


public class SharedVariable {

    private static int  sharedVariable = 0;// declare as volatile to make it work
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sharedVariable = 1;
            }
        }).start();

        for(int i=0;i<1000;i++) {
            for(;;) {
                if(sharedVariable == 1) {
                    break;
                }
            }
        }
        System.out.println("Value of SharedVariable : " + sharedVariable);
    }

}

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

Если вы запустите приведенный выше код, вы увидите, что он зависает бесконечно, потому что он никогда не видит обновленное значение sharedVariable.

Чтобы исправить код, объявите sharedVariable как volatile.

Почему обычная переменная не работает и вышеприведенная программа зависает?

  1. sharedVariable не был объявлен как volatile.
  2. Теперь, поскольку sharedVariable не была объявлена ​​как volatile, компилятор оптимизирует код. Он видит, что sharedVariable не будет изменен, так почему я должен читать из памяти каждый раз в цикле. Это выведет sharedVariable из цикла. Нечто похожее на ниже.

    for(int i=0i<1000;i++)/**compiler reorders sharedVariable
    as it is not declared as volatile
    and takes out the if condition out of the loop
    which is valid as compiler figures out that it not gonna  
    change sharedVariable is not going change **/
      if(sharedVariable != 1) {  
        for(;;) {}  
      }      
    }
    

Общий доступ на github: https://github.com/lazysun/concurrency/blob/master/Concurrency/src/com/snippets/SharedVariable.java

0 голосов
/ 07 января 2012

Что ж, получается, что все не так просто: когда вы печатаете в цикле, вы синхронизируете выходные потоки, и это включает в себя все ограждения памяти, так что вы фактически выходите.

Теперь вы не должны полагаться на такое поведение, но это означает, что если вы собираетесь продемонстрировать проблему, вы должны быть очень осторожны: многие системные вызовы, особенно ввод / вывод, будут включать скрытую синхронизацию, которая может испортить трюк. Таким образом, вы в конечном итоге говорите: «Это baaaaad» - и не в состоянии доказать это, что немного расстраивает.

В качестве примера кода, посмотрите , иллюстрирующий энергозависимость: является ли этот код поточно-ориентированным? (извините за бесстыдный плагин - просто у меня там только этот код).

...