Когда мы используем AtomicReference
?
Нужно ли создавать объекты во всех многопоточных программах?
Приведите простой пример использования AtomicReference.
источник
Когда мы используем AtomicReference
?
Нужно ли создавать объекты во всех многопоточных программах?
Приведите простой пример использования AtomicReference.
Атомная ссылка должна использоваться в настройках, где вам нужно выполнять простые атомарные (т. Е. Поточно- ориентированные, нетривиальные) операции над ссылкой, для которых синхронизация на основе монитора не подходит. Предположим, вы хотите проверить, является ли определенное поле только в том случае, если состояние объекта остается таким, как вы в последний раз проверяли
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
Из-за атомарной ссылочной семантики вы можете сделать это, даже если cache
объект является общим для потоков, без использования synchronized
. В общем, вам лучше использовать синхронизаторы или java.util.concurrent
фреймворк, чем голые, Atomic*
если вы не знаете, что делаете.
Две отличные ссылки о мертвом дереве, которые познакомят вас с этой темой:
Обратите внимание, что (я не знаю, было ли это всегда так), присвоение ссылки (то есть =
) само по себе является атомарным (обновление примитивных 64-битных типов, таких как long
или double
может не быть атомарным, но обновление ссылки всегда атомарно, даже если оно 64-битное ) без явного использования Atomic*
.
См. Спецификацию языка Java 3ed, Раздел 17.7 .
AtomicReference
вы должны пометить переменную,volatile
потому что, хотя среда выполнения гарантирует, что присвоение ссылки является атомарным, компилятор может выполнять оптимизацию в предположении, что переменная не изменялась другими потоками.AtomicReference
"; если вы будете использовать его, то мой совет будет идти в направлении , противоположного и пометить его ,final
так что компилятор может оптимизировать соответственно.Атомная ссылка идеальна для использования, когда вам нужно совместно использовать и изменять состояние неизменяемого объекта между несколькими потоками. Это очень плотное утверждение, поэтому я его немного разобью.
Во-первых, неизменный объект - это объект, который практически не изменяется после построения. Часто методы неизменяемого объекта возвращают новые экземпляры этого же класса. Некоторые примеры включают классы-обёртки Long и Double, а также String, и это лишь некоторые из них. (Согласно данным Programm Concurrency на JVM неизменяемые объекты являются важной частью современного параллелизма).
Далее, почему AtomicReference лучше, чем изменчивый объект для совместного использования этого общего значения. Простой пример кода покажет разницу.
Каждый раз, когда вы хотите изменить строку, на которую ссылается это изменчивое поле, основываясь на его текущем значении, вам сначала нужно получить блокировку для этого объекта. Это препятствует тому, чтобы какой-то другой поток вошел в это время и изменил значение в середине новой конкатенации строк. Затем, когда ваш поток возобновляется, вы забиваете работу другого потока. Но, честно говоря, этот код будет работать, он выглядит чистым и сделает большинство людей счастливыми.
Небольшая проблема. Это медленно. Особенно, если есть много разногласий по поводу этого объекта блокировки. Это связано с тем, что для большинства блокировок требуется системный вызов ОС, и ваш поток будет блокироваться и переключаться из ЦП, чтобы освободить место для других процессов.
Другой вариант - использовать AtomicRefrence.
Теперь, почему это лучше? Честно говоря, этот код немного менее чист, чем раньше. Но есть что-то действительно важное, что происходит под капотом в AtomicRefrence, это сравнение и обмен. Это одна команда процессора, а не вызов ОС, которая делает переключение возможным. Это одна инструкция на процессоре. А поскольку нет блокировок, в случае использования блокировки нет переключения контекста, что экономит еще больше времени!
Уловка в том, что для AtomicReferences здесь не используется вызов .equals (), а вместо этого == сравнение ожидаемого значения. Поэтому убедитесь, что ожидаемый фактический объект возвращен из цикла get.
источник
worked
чтобы получить ту же семантику.Вот пример использования для AtomicReference:
Рассмотрим этот класс, который действует как диапазон чисел и использует отдельные переменные AtmomicInteger для поддержания нижних и верхних границ чисел.
И setLower, и setUpper являются последовательностями проверки-затем-действия, но они не используют достаточную блокировку, чтобы сделать их атомарными. Если диапазон номеров содержит (0, 10), и один поток вызывает setLower (5), а другой поток вызывает setUpper (4), то с некоторым неудачным временем оба пройдут проверки в установщиках, и обе модификации будут применены. В результате диапазон теперь содержит (5, 4) недопустимое состояние. Таким образом, хотя базовые AtomicIntegers являются поточно-ориентированными, составной класс - нет. Это можно исправить с помощью AtomicReference вместо использования отдельных AtomicIntegers для верхних и нижних границ.
источник
Вы можете использовать AtomicReference при применении оптимистических блокировок. У вас есть общий объект, и вы хотите изменить его из более чем одного потока.
Поскольку другой поток мог изменить его и / может изменить между этими 2 шагами. Вы должны сделать это в атомарной операции. это где AtomicReference может помочь
источник
Вот очень простой пример использования, который не имеет ничего общего с безопасностью потоков.
Чтобы разделить объект между лямбда-вызовами,
AtomicReference
есть опция :Я не говорю, что это хороший дизайн или что-то в этом роде (это просто тривиальный пример), но если у вас есть случай, когда вам нужно разделить объект между лямбда-вызовами,
AtomicReference
опция является опцией.Фактически вы можете использовать любой объект, который содержит ссылку, даже коллекцию, которая имеет только один элемент. Тем не менее, AtomicReference идеально подходит.
источник
Я не буду много говорить. Мои уважаемые коллеги уже внесли свой ценный вклад.Полноценный исполняемый код в конце этого блога должен устранить любую путаницу. Речь идет о небольшой программе бронирования мест в кино по многопоточному сценарию.
Некоторые важные элементарные факты заключаются в следующем. 1> Различные потоки могут бороться только за экземплярные и статические переменные-члены в пространстве кучи. 2> Volatile чтение или запись полностью атомарны и сериализуются / происходят раньше и делаются только из памяти. Говоря это, я имею в виду, что любое чтение будет следовать за предыдущей записью в памяти. И любая запись будет следовать за предыдущим чтением из памяти. Таким образом, любой поток, работающий с volatile, всегда будет видеть самое актуальное значение. AtomicReference использует это свойство volatile.
Ниже приведены некоторые исходные коды AtomicReference. AtomicReference ссылается на ссылку на объект. Эта ссылка является переменной типа volatile в экземпляре AtomicReference, как показано ниже.
get () просто возвращает последнее значение переменной (как это происходит с волатильным способом «происходит раньше»).
Ниже приводится наиболее важный метод AtomicReference.
Метод compareAndSet (ожидание, обновление) вызывает метод compareAndSwapObject () небезопасного класса Java. Этот вызов метода unsafe вызывает собственный вызов, который вызывает единственную инструкцию для процессора. «ожидать» и «обновлять» каждая ссылка на объект.
В том и только в том случае, если переменная-член экземпляра AtomicReference «значение» ссылается на тот же объект, на который ссылается «ожидаемо», «обновление» теперь назначается этой переменной экземпляра, и возвращается «истина». Или же ложь возвращается. Все это сделано атомарно. Никакой другой поток не может перехватить между ними. Поскольку это однопроцессорная операция (магия современной компьютерной архитектуры), она часто быстрее, чем использование синхронизированного блока. Но помните, что когда несколько переменных необходимо обновить атомарно, AtomicReference не поможет.
Я хотел бы добавить полноценный работающий код, который можно запустить в Eclipse. Это очистило бы много беспорядка. Здесь 22 пользователя (темы MyTh) пытаются забронировать 20 мест. Ниже приведен фрагмент кода с последующим полным кодом.
Фрагмент кода, где 22 пользователя пытаются забронировать 20 мест.
Ниже приводится полный код.
источник
AtomicReference - это гибкий способ атомарного обновления значения переменной без использования синхронизации.
AtomicReference
поддержка безпотокового программирования без блокировок для отдельных переменных.Существует несколько способов достижения безопасности потоков с помощью высокоуровневого параллельного API. Атомарные переменные - это один из нескольких вариантов.
Lock
Объекты поддерживают идиомы блокировки, которые упрощают многие параллельные приложения.Executors
определить высокоуровневый API для запуска и управления потоками. Реализации исполнителя, предоставляемые java.util.concurrent, обеспечивают управление пулом потоков, подходящее для крупномасштабных приложений.Параллельные коллекции облегчают управление большими коллекциями данных и могут значительно снизить потребность в синхронизации.
Атомные переменные имеют функции, которые минимизируют синхронизацию и помогают избежать ошибок согласованности памяти.
Пример кода с
AtomicReference
:Вам не нужно использовать
AtomicReference
во всех многопоточных программах.Если вы хотите защитить одну переменную, используйте
AtomicReference
. Если вы хотите защитить блок кода, используйте другие конструкции, такие какLock
/synchronized
и т. Д.источник
Другой простой пример - модификация безопасного потока в объекте сеанса.
Источник: http://www.ibm.com/developerworks/library/j-jtp09238/index.html
источник