Предисловие
В предыдущей статье говорилось о принципе CAS, в котором упоминался атомный* класс. Механизм реализации атомных операций зависит от характеристик видимости памяти волатильной. Если вы еще не знаете CAS и Atomic*, рекомендуется взглянуть на то, о чем CAS Spin Block мы говорим.
Три характеристики параллелизма
Прежде всего, если мы хотим использовать летучие, это должно быть в многопоточной среде параллелизма. В одновременном сценарии есть три важных характеристик, о которых мы часто говорим: атомность, видимость и упорядоченность. Только когда эти три характеристики будут выполнены, можно правильно выполнять одновременную программу, в противном случае возникают различные проблемы.
Атомность, CAS и атомные* классы, упомянутые в предыдущей статье, могут обеспечить атомацию простых операций. Для некоторых ответственных операций он может быть реализован с использованием синхронизированных или различных замков.
Видимость относится к тому, когда несколько потоков получают доступ к одной и той же переменной, один поток изменяет значение переменной, а другие потоки могут немедленно увидеть измененное значение.
Заказ, порядок выполнения программы выполняется в порядке кода, а инструкциям запрещено переупорядочить. Кажется естественным, что это не так. Перезаряжение инструкций-это JVM для оптимизации инструкций и повышения эффективности работы программы и максимально улучшить параллелизм, не влияя на результаты выполнения однопользованной программы. Однако в многопоточной среде порядок некоторых кодов может вызвать логическую неправильность.
Волвалование реализует две функции, видимость и порядок. Следовательно, в многопоточной среде необходимо обеспечить функцию этих двух функций, и можно использовать летучивое ключевое слово.
Насколько изменчивой гарантией видимости
Когда дело доходит до видимости, вам нужно понять процессор компьютера и основную память. Из-за мультиподбота, независимо от того, сколько их потоков, он в конечном итоге будет выполнен в компьютерном процессоре. Сегодняшние компьютеры в основном многоязычные, а некоторые машины даже имеют многопроцессоры. Давайте посмотрим на диаграмму структуры многопроцессора:
Это процессор с двумя процессорами, четырехъядерный. Процессор соответствует физическому слоту, и несколько процессоров подключены через шину QPI. Процессор состоит из нескольких ядер и многоядерного общего кэша L3 между процессорами. Ядро содержит регистры, кэш L1, кэш L2.
Во время выполнения программы необходимо задействовать чтение и написание данных. Мы все знаем, что, хотя скорость доступа к памяти уже очень быстрая, она все еще уступает скорости инструкций по выполнению ЦП. Следовательно, в ядре, L1, L2 и L3 -уровня добавляются кэши третьего уровня. Таким образом, когда программа работает, необходимые данные сначала копируются из основной памяти в кеш ядра, и после завершения операции он затем записывается в основную память. На следующем рисунке представлена схематическая схема данных доступа к ЦП, от регистров до кэша, основной памяти и даже жестких дисков, скорость становится медленнее и медленнее.
Понимая структуру процессора, давайте посмотрим на конкретный процесс выполнения программы и сделаем простую операцию самостоятельной интриги в качестве примера.
i = i+1;
При выполнении этого оператора поток, работающий на ядре, копирует значение I в кэш, где находится ядро. После того, как операция будет завершена, она будет написана обратно в основную память. В многопоточной среде каждый поток будет иметь соответствующую рабочую память в области кеша в работающем ядре, то есть в каждом потоке есть своя собственная частная область рабочего кэша для хранения данных реплики, необходимых для операции. Затем давайте посмотрим на проблему I+1. Предполагая, что начальное значение I равен 0, есть два потока, которые выполняют этот оператор одновременно, и каждому потоку нужно три шага для выполнения:
1. Прочитайте значение I из основной памяти в рабочую память потока, то есть соответствующую область кэша ядра;
2. Рассчитайте значение I+1;
3. Напишите значение результата обратно в основную память;
После того, как два потока выполнены по 10 000 раз каждый, ожидаемая стоимость должна составлять 20 000. К сожалению, стоимость I всегда составляет менее 20 000. Одной из причин этой проблемы является проблема согласованности кэша. Для этого примера, как только кеш -копия потока изменяется, копия кэша других потоков должна быть признана немедленно.
После использования нестабильного ключевого слова будут следующие эффекты:
1. Каждый раз, когда переменная изменяется, кэш процессора (рабочая память) будет записан в основную память;
2. Написание в основную память рабочей памяти приведет к неверным кеша процессора (рабочей памяти) других потоков.
Поскольку летучая часть обеспечивает видимость памяти, он фактически использует протокол MESI, который обеспечивает согласованность кэша с помощью ЦП. Есть много содержимого протокола MESI, поэтому я не буду объяснять это здесь. Пожалуйста, проверьте это самостоятельно. Короче говоря, используется нестабильное ключевое слово. Когда модификация потока в летучую переменную будет немедленно записана обратно в основную память, в результате чего линия кэша других потоков будет недействительна, а другие потоки вынуждены снова использовать переменную, ее необходимо прочитать из основной памяти.
Затем мы изменяем приведенную выше переменную с помощью изменчивой и выполняем ее снова, каждый поток будет выполнять 10 000 раз. К сожалению, это все еще менее 20 000. Почему это?
«Волатильный» использует протокол MESI процессора для обеспечения видимости. Тем не менее, обратите внимание, что летучая часть не гарантирует атомичность операции, потому что эта операция самостоятельного достижения разделена на три этапа. Предположим, что поток 1 считывает значение I из основной памяти, предполагая, что оно 10, и в настоящее время происходит закупорка, но я еще не был изменен. В настоящее время поток 2 также читает значение I из основной памяти. В настоящее время значение I значения, прочитанное этими двумя потоками, одно и то же, оба 10, а затем поток 2 добавляет 1 к I и немедленно записывает его обратно в основную память. В настоящее время, согласно протоколу MESI, линия кэша, соответствующая рабочей памяти потока 1, будет установлена в неверное состояние, да. Тем не менее, обратите внимание, что поток 1 уже скопировал значение I из основной памяти, и теперь он требует только операции добавления 1 и записи обратно в основную память. Оба потока добавляют 1 на основе 10, а затем записывают в основную память, поэтому конечное значение основной памяти составляет всего 11, а не ожидаемые 12.
Следовательно, использование летучих людей может обеспечить видимость памяти, но это не может гарантировать атомность. Если атомность все еще необходима, вы можете обратиться к этой предыдущей статье.
Насколько нестабильный обеспечивает порядок
Модель памяти Java имеет некоторую врожденную «Lines Line», то есть ее можно гарантировать без каких -либо средств. Обычно это называется принцип «происходит». Если порядок выполнения двух операций не может быть получен из принципа «произойти», то они не могут гарантировать их упорядоченность, и виртуальные машины могут переупорядочить их по желанию.
Ниже приведены 8 принципов, прежде всего, выдержали из «подробного понимания виртуальных машин Java».
Здесь мы в основном поговорим о правилах изменчивого ключевого слова и приведем пример двойной проверки в знаменитом шаблоне Синглтона:
Класс Singleton {Private volatile Static Singleton Extance = null; private singleton () {} public static singleton getInstance () {if (exance == null) {// step 1 synchronized (singleton.class) {if (ancess == null) // step 2 encement = new singleton (); // Шаг 3}} return Encement; }}Если экземпляр не изменен с помощью летучих, какие результаты могут быть получены? Предположим, есть два потока, вызывающих метод GetInstance (). Поток 1 выполняет Step1 и обнаруживает, что экземпляр нулю, а затем синхронно блокирует класс Синглтона. Затем определяет, является ли экземпляр снова нулевой, и обнаруживает, что он все еще нуль, а затем выполняет шаг 3 и начинает создавать синглтон. Во время процесса экземпляра поток 2 переходит на шаг 1 и может обнаружить, что экземпляр не является пустым, но в настоящее время экземпляр может быть не полностью инициализирован.
Что это значит? Объект инициализируется в трех шагах и представлен следующим псевдокодом:
memory = allocate (); // 1. Распределить пространство памяти объекта ctorinstance (память); // 2. Инициализировать экземпляр объекта = память; // 3. Установите пространство памяти объекта, указывающего на объект
Поскольку шаг 2 и шаг 3 должны зависеть от шага 1, а шаг 2 и шаг 3 не имеют зависимости, возможно, что эти два утверждения будут подвергаться повторному прибору инструкций, или возможно, что шаг 3 будет выполнен до шага 2. В этом случае шаг 3 выполнен, но шаг 2 еще не выполнен, то есть, экземпляр экземпляра еще не был инициализирован. Только сейчас, нить 2 судит, что экземпляр не является нулевым, поэтому он непосредственно возвращает экземпляр экземпляра. Однако в настоящее время экземпляр на самом деле является неполным объектом, поэтому при его использовании будут проблемы.
Использование летучих ключевых слов означает использование принципа «написания переменной, измененной летучим, прежде чем прочитать переменную в любое последующее время», соответствует приведенному выше процессу инициализации. Шаги 2 и 3 - это пишущие экземпляры, поэтому они должны произойти позже, когда чтение экземпляров, то есть не будет возможности вернуть экземпляр, который не полностью инициализирован.
Основной JVM делается через то, что называется «барьером памяти». Барьер памяти, также известный как забор памяти, представляет собой набор инструкций процессора, используемых для реализации последовательных ограничений на операции памяти.
наконец
Благодаря нестабильному ключевому слову мы узнали о видимости и упорядоченности в одновременном программировании, что, конечно, является просто простой пониманием. Для более глубокого понимания вы должны полагаться на своих одноклассников, чтобы изучить его самостоятельно.
Связанные статьи
О чем мы говорим
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.