Платальная переменная обеспечивает видимость потока и не гарантирует безопасность и атомность потока.
Что такое видимость потока:
Замки предоставляют две основные особенности: взаимное исключение и видимость. Взаимное исключение означает, что только один поток может содержать определенную блокировку за раз, поэтому эта функция может использоваться для реализации протокола скоординированного доступа для общих данных, чтобы только один поток мог использовать общие данные одновременно. Видимость немного сложнее, и она должна обеспечить, чтобы изменения в общих данных до выпуска блокировки были видны в другой потоке, который впоследствии получает блокировку - без этой гарантии видимости, предоставленной механизмом синхронизации, общие переменные, наблюдаемые по потоке, могут быть предварительно модифицированными или противоречивыми значениями, которые вызовут много серьезных проблем.
Смотрите семантику летучих :
Волатильный эквивалент слабой реализации синхронизированной, что означает, что летучие реализует синхронизированную семантику, но не имеет механизма блокировки. Это гарантирует, что обновления в нестабильном поле информируют другие потоки предсказуемым образом.
Волатильный содержит следующую семантику:
(1) Модель хранения Java не будет переупорядочить операции валатильных инструкций: это гарантирует, что операции на летучие переменные выполняются в порядке появления инструкций.
(2) Волатильная переменная не будет кэширована в реестре (видно только потоки) или в других местах, которые не видны для ЦП. Результат летучей переменной всегда каждый раз читается из основной памяти. Другими словами, для модификации летучей переменной всегда видны другие потоки, и они не используют переменные внутри стека потоков. То есть, в законе о случаях, после написания валатильной переменной, любая последующая операция чтения может быть понята, чтобы увидеть результат этой операции записи.
Хотя характеристики летучей переменной хороши, летучие не может гарантировать безопасность потока. То есть работа летучего поля не является атомной. Улепательная переменная может гарантировать только видимость (другие потоки могут понять результаты после того, как увидев это изменение после изменения одного потока). Чтобы обеспечить атомность, вы можете только заблокировать его до сих пор!
Принципы использования летучих:
Три принципа применения летучих переменных:
(1) Переменные записи не зависят от значения этой переменной, или только один поток изменяет эту переменную
(2) Состояние переменной не нужно участвовать в инвариантных ограничениях с другими переменными
(3) переменные доступа не нужно заблокировать
Фактически, эти условия указывают на то, что эти допустимые значения, которые могут быть записаны в летучую переменную, не зависят от состояния любой программы, включая текущее состояние переменной.
Первые ограничения условия предотвращают использование летучих переменной в качестве противоречивого счетчика. Хотя инкрементная операция (x ++) выглядит как отдельная операция, на самом деле это комбинированная операция, состоящая из последовательности операций с модификацией чтения-модификации-записи, которая должна выполняться атомно, а летучие не могут обеспечить необходимые атомные свойства. Реализация правильной операции требует сохранения значения x постоянной во время операции, что невозможно при летучей переменной. (Однако, если значение корректируется, чтобы записаться только из одного потока, первое условие можно игнорировать.)
Большинство ситуаций программирования вступают в противоречие с одним из этих трех условий, что делает летучую переменную не так, как универсально применима к безопасности потока, как синхронизированная. В листинге 1 показан безболезный класс численного диапазона. Он содержит инвариант - нижняя граница всегда меньше или равна верхней границе.
Правильно использовать летучие:
Режим № 1: Флаги состояния
Возможно, спецификация реализации изменчивой переменной состоит в том, чтобы просто использовать логический флаг состояния, чтобы указать, что произошло важное одноразовое событие, такое как завершение инициализации или запрос времени простоя.
Многие приложения включают в себя структуру управления в форме «выполнить некоторую работу, когда программа не готова остановить», как показано в списке 2:
Листинг 2. Используйте летучую переменную в качестве флага состояния
Волатильный логический выключаемый; … Public void shutdown () {shutdownrequest = true; } public void dowork () {while (! shutdownRequest) {// do Stuff}}Весьма вероятно, что метод Shutdown () будет вызван из -за пределов петли, то есть в другом потоке, поэтому необходимо выполнить какой -то вид синхронизации, чтобы убедиться, что видимость отключенной переменной правильно реализована. (Может быть вызвано от JMX Alluster, Operation Hellocer в потоке событий GUI, через RMI, через веб -сервис и т. Д.). Тем не менее, написание цикла с синхронизированным блоком гораздо более хлопотно, чем написание с флагом летучего состояния, показанном в листинге 2. Поскольку летучие упрощает кодирование, а флаги состояния не связаны с любыми другими состояниями в рамках программы, он очень подходит для летучих здесь.
Общей особенностью такого типа тега состояния является то, что обычно существует только один переход состояния; Флаг с закрытием преобразуется из False в True, и программа останавливается. Этот шаблон может быть расширен на флаг состояния перехода вперед и назад, но может быть расширен только в случае не замечен переходного периода (от False до TRUE, затем до false). Кроме того, требуются некоторые механизмы конверсии атомного состояния, такие как атомные переменные.
Режим № 2: Единственная безопасная публикация
Отсутствие синхронизации может привести к недостижимой видимости, что затрудняет определение того, когда писать ссылку на объект вместо примитивного значения. В отсутствие синхронизации может быть встречено обновленное значение, на которое ссылается объект (написанный другим потоком), и старое значение состояния этого объекта существует одновременно. (Это основная причина знаменитой задачи с двумя проверенными заблокированными, в которой ссылки на объекты читаются без синхронизации, что приводит к проблеме, что вы можете увидеть обновленную ссылку, но все же видите не полностью сконструированный объект через эту ссылку).
Одним из методов реализации безопасной публикации объектов является определение ссылок объектов как летучие типы. В листинге 3 показан пример, в котором фоновый поток загружает некоторые данные из базы данных во время запуска. Другие коды, когда они могут использовать эти данные, проверяют, был ли они опубликованы перед использованием.
Листинг 3. Использование летучей переменной для единовременного безопасного выпуска
Public Class FounalFloobleLoader {public volatile Flooble The Flooble; public void initinbackground () {// делать много вещей theflooble = new Flooble (); // Это единственная запись The Flooble}} public Class SomeTherClass {public void dowork () {while (true) {// Делать кое -что… // Использовать флуза, но только если он готов (floobleloader.theflooble! }}}Если обширная ссылка не является нестабильным типом, код в dowork () получит не полностью построенный вкупл, когда он не является оболочками.
Необходимым условием для этого шаблона является то, что опубликованный объект должен быть безопасным для потока, или действительный имметируемый объект (эффективное неизменное означает, что состояние объекта никогда не будет изменено после публикации). Ссылки на летучую тип обеспечить видимость формы публикации объекта, но требуется дополнительная синхронизация, если состояние объекта изменится после публикации.
Паттерн № 3: Независимое наблюдение
Еще один простой режим использования летучих целей - регулярно «выпускать» наблюдения для внутреннего использования программы. Например, предположим, что существует датчик окружающей среды, способный ощущать температуру окружающей среды. Фоновый поток может читать датчик каждые несколько секунд и обновлять летучую переменную, содержащую текущий документ. Затем другие потоки могут прочитать эту переменную, чтобы они могли видеть последнее значение температуры в любое время.
Другое приложение, которое использует этот режим, - это сборы статистики программы. Список 4 показывает, как механизм аутентификации помнит имя пользователя, который был вошел в систему в последний раз. Повторите ссылку на тренажеру для публикации значений для других частей программы.
Листинг 4. Использование изменчивой переменной для публикации нескольких независимых наблюдений
Public Class UserManager {public volatile String tastleUser; Public Boolean Authenticate (String User, String Password) {boolean valive = passwordIsvalid (пользователь, пароль); if (advite) {user u = new user (); ActiveUsers.add (u); TasterUser = пользователь; } return Valid; }}Этот режим является расширением предыдущего режима; Публикация определенного значения для использования в других местах программы, но в отличие от публикации одноразового события, это серия независимых событий. Этот шаблон требует, чтобы опубликованное значение было достоверным и неизменным, то есть статус значения не изменится после публикации. Код, который использует это значение, должен быть понятным, что значение может измениться в любое время.
Режим № 4: режим «летучих бобов»
Платальный шаблон бобов подходит для рамок, которые используют Javabeans как «структуру чести». В рисунке летучих бобов джавабцы используются в качестве набора контейнеров с независимыми свойствами методов Getter и/или Setter. Основной принцип шаблона летучих бобов заключается в том, что многие рамки предоставляют контейнеры для держателей летучих данных (таких как HTTPSession), но объекты, размещенные в этих контейнерах, должны быть безопасными для резьбы.
В режиме летучих бобов все члены данных Javabean имеют летучую тип, а методы Getter и Setter должны быть очень обычными - они не могут содержать какую -либо логику, кроме как получить или установить соответствующие свойства. Кроме того, для членов данных, на которые ссылается объект, ссылочный объект должен быть допустимым и неизменным. (Это запретит свойства со значениями массива, потому что, когда ссылка на массив объявляется как летучая, только ссылка, а не сама массива имеет летучую семантику). Для любой нестабильной переменной инварианты или ограничения не могут содержать Javabean Properties. Примеры в листинге 5 показывают джавабейны, которые придерживаются шаблона летучих бобов:
Режим № 4: режим «летучих бобов»
@ThreadSafe Public Class Person {Private volatile String FirstName; частная летучая строка Lastname; частный летучий возраст; public String getFirstName () {return FirstName; } public String getLastName () {return LastName; } public int getage () {return Age; } public void setFirstName (String FirstName) {this.FirstName = FirstName; } public void setlastName (String lastname) {this.lastName = lastName; } public void setage (int age) {this.age = age; }}Расширенный режим летучего
Паттерны, описанные в предыдущих разделах, охватывают большинство основных вариантов использования, и использование летучих в этих шаблонах очень полезно и просто. В этом разделе представлен более продвинутый режим, в котором волатильный будет обеспечивать преимущества производительности или масштабируемости.
Расширенные режимы нестабильных приложений очень хрупкие. Следовательно, предположения должны быть тщательно доказаны, и эти шаблоны строго инкапсулируются, потому что даже очень небольшие изменения могут испортить ваш код! Аналогичным образом, причина использования более продвинутого варианта использования состоит в том, что он может улучшить производительность, гарантируя, что вы действительно определяете, что вам необходимо достичь этой выгоды от производительности, прежде чем начать применение расширенных шаблонов. Существуют компромиссы на эти модели, отказываясь от читаемости или обслуживания в обмен на возможный рост производительности-если вам не нужны улучшения производительности (или вы не можете доказать, что вам это нужно с помощью строгой программы тестирования), то это, вероятно, будет плохой сделкой, потому что вы, вероятно, потеряете меньше денег и получите что-то, что вы сдаете.
Режим № 5: Стратегия блокировки чтения-записи с низкими накладными расходами
До сих пор вы должны понимать, что летучие не достаточно для реализации счетчиков. Поскольку ++ x на самом деле является простой комбинацией трех операций (читать, добавить, хранить), если несколько потоков пытаются выполнить инкрементные операции на летучих счетчике одновременно, его обновленное значение может быть потеряно.
Однако, если операция чтения гораздо больше, чем операция записи, вы можете использовать внутреннюю блокировку и летучие переменные, чтобы уменьшить накладные расходы общедоступного пути кода. Счетчики, защищенные потоком, показанные в списке 6, используют синхронизированный, чтобы гарантировать, что инкрементные операции являются атомальными и летучими, чтобы обеспечить видимость текущего результата. Этот метод может достичь лучшей производительности, если обновления не часты, потому что накладные расходы на пути чтения включают только операцию летучих чтения, что обычно лучше, чем накладные расходы безрезультатного приобретения блокировки.
Листинг 6. Используйте летучие и синхронизированные для достижения «замков с чтением нижних накладных расходов»
@ThreadSafe public Class CheesyCounter {// использует дешевый трюк с чтением-Write // Все мутативные операции должны быть выполнены с «этой» блокировкой @Guardedby («This») частного волатильного значения Int; public int getValue () {return Value; } public synchronized int urgment () {return value ++; }}Причина, по которой этот метод называется «блокировкой чтения с чтением нижних накладных расходов», заключается в том, что вы используете другой механизм синхронизации для операций чтения. Поскольку операция записи в этом примере нарушает первое условие использования летучих, счетчик не может быть безопасно реализован с помощью летучих - вы должны использовать блокировку. Тем не менее, вы можете использовать летучие в операциях чтения, чтобы обеспечить видимость текущего значения, чтобы вы могли использовать блокировки для выполнения всех изменений и только для чтения с помощью летучих. Среди них блокировка позволяет только одному потоку получать доступ к значению за раз, а волатильный позволяет нескольким потокам выполнять операции чтения. Следовательно, при использовании летучего, чтобы убедиться, что путь кода прочитал, он является большим обменом, чем использованием блокировки для выполнения всех путей кода - точно так же, как операция чтения. Тем не менее, имейте в виду недостатки этой модели: если самое основное применение этой модели не превышает, станет очень трудно объединить эти два конкурирующих механизмы синхронизации.
О переупорядочке инструкций и правиле, предварительно
1. Пусть переупорядочение
Спецификация языка Java предусматривает, что поток JVM поддерживает последовательную семантику внутри, то есть до тех пор, пока конечный результат программы эквивалентен ее результатам в строгой последовательной среде, порядок выполнения инструкций может быть несовместимым с порядком кода. Этот процесс переупорядочен командой. Значение повторного порядок инструкций состоит в том, что JVM может надлежащим образом переупорядочить инструкции машины в соответствии с характеристиками процессора (многоуровневая система CPU, многоядерный процессор и т. Д.), Таким образом, инструкции машины в большей степени соответствуют характеристикам выполнения CPU и максимизируют производительность машины.
Самая простая модель для выполнения программы состоит в том, чтобы выполнить в порядке появления инструкций, который не зависит от процессора, который выполняет инструкции, обеспечивая переносимость инструкций в наибольшей степени. Профессиональный термин для этой модели называется моделью последовательной последовательности. Тем не менее, современные компьютерные системы и архитектуры процессоров не гарантируют это (потому что искусственное обозначение не всегда может гарантировать соблюдение характеристик обработки процессора).
2. Appens-Before Правило
Модель хранения Java имеет принцип, прежде всего, то есть, если действие B хочет увидеть результат выполнения действия A (независимо от того, выполняется ли A/B в одном и том же потоке), то A/B должен удовлетворить отношения, прежде всего.
Прежде чем представить правило «Tear-Before», введите концепцию: JMM Action (Java Memeory Model Action), Java сохраняет модельные действия. Действие включает в себя: переменные считывания и записи, блокировки блокировки и выпуска монитора, запуск потока () и join (). Замок будет упомянут позже.
случается, прежде чем полные правила:
(1) Каждое действие в одном и том же потоке происходит до любого действия, которое появляется после него.
(2) Разблокировка монитора происходит до каждого последующего блокировки на одном и том же мониторе.
(3) Операция записи в нестабильное поле происходит до каждой последующей операции чтения того же поля.
(4) Вызов Thread.Start () произойдет, прежде чем действия в потоке запуска.
(5) Все действия в потоке случаются, прежде чем проверяют другие потоки, чтобы завершить этот поток или вернуть в потоке. JOIN () или Think.isalive () == false.
(6) Один поток A вызывает прерывание () другого потока B, прежде чем резьба A обнаруживает, что B прерывается (B вызывает исключение или обнаружение b ISeReverrupted () или прерывание ()).
(7) Конец конструктора объекта происходит до того, как и начало финализатора объекта
(8) Если действие происходит, прежде чем в действии B, а действие B происходит до действия и C, то действие происходит перед действием C.
Выше всего об этой статье. Я представлю это вам здесь. Я надеюсь, что вам будет полезно изучить и понять летучие переменные на Java.