Неустойчиво или синхронизировано для примитивного типа?

В 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-битные значения".