java - Связаны ли статические переменные между потоками?




multithreading concurrency (5)

Мой преподаватель в классе java на верхнем уровне в потоке сказал что-то, о чем я не знал.

Он заявил, что следующий код не обязательно будет обновлять ready переменную. По его словам, эти два потока не обязательно разделяют статическую переменную, особенно в том случае, когда каждый поток (основной поток по сравнению с ReaderThread) работает на своем собственном процессоре и поэтому не использует одни и те же регистры / кеш / etc и один CPU не будет обновлять другой.

По сути, он сказал, что возможно, что ready обновляется в основном потоке, но НЕ в ReaderThread, так что ReaderThread будет циклически работать бесконечно. Он также утверждал, что программа может напечатать «0» или «42». Я понимаю, как можно напечатать «42», но не «0». Он упомянул, что это будет случай, когда number переменная установлена ​​на значение по умолчанию.

Я думал, что, возможно, не гарантировано, что статическая переменная обновляется между потоками, но это кажется мне очень странным для Java. Устанавливает ли ready волатильности исправлять эту проблему?

Он показал этот код:

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;  
    }  
}

@dontocsata вы можете вернуться к своему учителю и немного его обучить :)

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

Следующие две переменные будут находиться в одной и той же строке кэша практически любой архитектуры знаний.

private static boolean ready;  
private static int number;  

Thread.exit (основной поток) гарантированно Thread.exit выход, и exit гарантированно вызывает забор памяти из-за удаления потока нитей группы (и многих других проблем). (это синхронизированный вызов, и я не вижу, чтобы один способ не был реализован без синхронизации, так как ThreadGroup также должна завершиться, если не осталось ни одного потока демонов и т. д.).

ReaderThread поток ReaderThread будет продолжать поддерживать этот процесс, поскольку он не является демоном! Таким образом, ready и number будет сбрасываться вместе (или число до этого, если произойдет контекстный переключатель), и нет реальной причины для переупорядочения в этом случае, по крайней мере, я даже не могу думать об одном. Вам понадобится что-то действительно странное, чтобы увидеть что-либо, кроме 42 . Опять же, я полагаю, что и статические переменные будут в одной строке кэша. Я просто не могу представить себе строку кэша длиной 4 байта или JVM, которая не будет назначать их в непрерывной области (кэш-строке).


В принципе это правда, но на самом деле проблема сложнее. Видимость общих данных может быть затронута не только кэшами ЦП, но и нарушением исполнения инструкций.

Поэтому Java определяет модель памяти , которая указывает, при каких обстоятельствах потоки могут видеть согласованное состояние общих данных.

В вашем конкретном случае добавление volatile гарантирует видимость.


Когда вы инициализируете статический примитивный тип переменной, java default присваивает значение для статических переменных

public static int i ;

когда вы определяете такую ​​переменную как значение по умолчанию i = 0; вот почему есть возможность получить вас 0. тогда основной поток обновляет значение boolean ready to true. так как готово статическая переменная, основной поток и другая ссылка на один и тот же адрес памяти, так что переменная готова. поэтому вторичный поток выходит из цикла while и print. при печати значения инициализированное значение числа равно 0. Если процесс потока прошел в цикле до переменной номера обновления основного потока. то есть возможность распечатать 0


Нет проблемы видимости, характерной для статических переменных. Существует проблема видимости, навязанная моделью памяти JVM. Вот статья о модели памяти и о том, как записи становятся видимыми для потоков . Вы не можете рассчитывать на изменения, которые один поток становится видимым для других потоков своевременно (на самом деле JVM не обязана делать эти изменения видимыми для вас вообще), если вы не установили связь между событиями , вот цитата из эта ссылка (приведенная в комментарии Джеда Уэсли-Смита):

В главе 17 Спецификации языка Java определяется отношение «происшествия до операции» в операциях памяти, таких как чтение и запись общих переменных. Результаты записи одним потоком гарантированно будут видны для чтения другим потоком только в том случае, если операция записи происходит до операции чтения. Синхронизированные и изменчивые конструкции, а также методы Thread.start () и Thread.join () могут формироваться в отношениях-до отношений. В частности:

  • Каждое действие в потоке происходит - перед каждым действием в этом потоке, которое приходит позже в заказе программы.

  • Происходит разблокировка (синхронизированный блок или выход метода) монитора перед каждой последующей блокировкой (синхронизированный блок или ввод метода) того же монитора. И поскольку отношение «происхождение-до» транзитивно, все действия потока до разблокировки происходят до всех действий, следующих за любой блокировкой потока, которые контролируют.

  • Происходит запись в нестабильное поле - перед каждым последующим чтением этого же поля. Записи и чтения изменчивых полей имеют аналогичные эффекты согласованности с памятью как входные и выходящие мониторы, но не влекут за собой блокировку взаимного исключения.

  • Вызов для начала в потоке происходит - перед любым действием в запущенном потоке.

  • Все действия в потоке происходят, прежде чем какой-либо другой поток успешно вернется из соединения в этом потоке.


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

И теоретически записи, сделанные другим потоком, могут оказаться в другом порядке, если переменные не объявлены volatile или записи явно синхронизированы.





memory-visibility