Неустойчиво или синхронизировано для примитивного типа?
В Java назначение является атомарным, если размер переменной меньше или равен 32 битам, но не больше, чем 32 бита.
Что (volatile/synchronized) было бы более эффективным для использования в случае двойного или длительного назначения?
Как
volatile double x = y;
synchronized не применимо с примитивным аргументом. Как использовать синхронизацию в этом случае? Конечно, я не хочу блокировать свой класс, поэтому this
не следует использовать.
Ответы
Ответ 1
Если вы обнаружите, что блокировка на самом объекте слишком тяжелая, синхронизация - это путь. До Java 1.5 volatile, возможно, был хорошим выбором, но теперь волатильность может иметь очень большое влияние, заставляя упорядочивать инструкции по методу, в котором происходит присвоение. Создайте отдельный объект (private final Object X_LOCK = new Object();
) и синхронизируйте его при настройке или получении значения этого двойника. Это даст вам прекрасный уровень контроля за блокировкой, который, по-видимому, вам нужен.
В новом пакете concurrency есть больше параметров, таких как AtomicReference, которые могут быть хорошей заменой для volatile, если вам действительно нужно избегать синхронизации.
Ответ 2
Что вы пытаетесь сделать? Ключевыми словами synchronized
и volatile
являются механизмы в Java, которые могут использоваться для обеспечения того, чтобы согласованные значения наблюдались разными потоками, читающими одни и те же данные. В частности, они позволяют вам рассуждать о случившемся до отношений в ваших программах.
Вы просто не можете избежать использования одного из volatile
или synchronized
для правильного доступа к полям не final
в многопоточной программе. Тем не менее, главная причина, по которой вам, скорее всего, потребуется synchronized
over volatile
, - это требование использования операций атомного сравнения и набора (т.е. не будет никакого учета производительности). Например, в многопоточной программе:
volatile int i = 0;
public void foo() {
if (i == 0) i = i + 1;
}
Вышеприведенный код по своей сути небезопасен, хотя объявление переменной как изменчивое означает, что чтение и запись сбрасываются в основную память - единственная безопасная реализация такого метода будет выглядеть примерно так:
int i = 0;
public synchronized void foo() {
if (i == 0) i = i + 1;
}
Итак, что вы предпочитаете? Ну, если у вас есть несколько потоков, изменяющих поле, зависящее от этого значения поля (т.е. Сравнение и набор), тогда synchronized
является единственным безопасным решением.
Также стоит сказать: накладные расходы на производительность synchronized
не проблема (в подавляющем большинстве случаев). Проблемы с синхронизацией обычно связаны с ненужными узкими местами кода, взаимоблокировками или живыми потоками, и при необходимости их можно смягчить. Любые издержки на чистые тактовые циклы будут затмеваны другими вашими приложениями: файлом IO, запросами базы данных, удалением и т.д.
Ответ 3
volatile - это, безусловно, путь, если вы выполняете только задание.
Я уверен, что вы знаете, но так как он был взят: если вы хотите сделать более сложные операции (увеличивайте значение, например), вам нужно будет синхронизировать. я ++ никогда не является потокобезопасным для любого типа переменных. Вам нужно синхронизировать. я ++ и тому подобное, поскольку на самом деле это более чем 1 операция.
Нет: было выражено, что вы можете использовать AtomicDouble, но в настоящее время AtomicDouble отсутствует в java.util.concurrent.atomic
Если вы выполняете многократные операции с x, для чего требуется установить его в новое значение в конце, это можно сделать безопасным потоком без блокировки, что бы это ни было, и обеспечить безопасность потока, используя сравнение и набор. Пример:
AtomicLong x = new AtomicLong(SomeValue);
public void doStuff() {
double oldX;
double newX;
do {
oldX = x.get();
newX = calculateNewX(oldX);
} while (!x.compareAndSet
(Double.doubleToLongBits(oldX), Double.doubleToLongBits(newX)));
Это работает, потому что compareAndSet увидит, изменилось ли значение x с момента последнего его чтения. Если x изменился, вам придется снова выполнить вычисление и повторить попытку установки.
Вы могли бы, конечно, реализовать свой собственный AtomicDouble вместо выполнения этих преобразований doubleToLongBits. Взгляните на AtomicFieldUpdater.
Ответ 4
KDSRathore, вы можете использовать некоторые явные блокировки или сделать некоторый фиктивный объект Object = new Object(), на котором вы синхронизируете в setter/getter этого двойного
Ответ 5
Согласно документация оракула, вы можете использовать volatile для ссылки на Double object:
volatile Double x = y;
"Записи и чтения ссылок всегда являются атомарными, независимо от того, реализованы ли они как 32-битные или 64-битные значения".