Изменяемый или синхронизированный для примитивного типа?

StackOverflow https://stackoverflow.com/questions/1779258

Вопрос

В Java присваивание является атомарным, если размер переменной меньше или равен 32 битам, но не является атомарным, если больше 32 бит.

Что (volatile / synchronized) было бы более эффективно использовать в случае двойного или длительного назначения?

Нравится,

  volatile double x = y;

synchronized неприменим к примитивному аргументу.Как мне использовать synchronized в этом случае?Конечно, я не хочу запирать свой класс, так что this не следует использовать.

Это было полезно?

Решение

Если вы считаете, что блокировка самого объекта слишком трудна, тогда лучше использовать синхронизацию.До версии Java 1.5 летучее значение, возможно, было хорошим выбором, но теперь оно может иметь очень большое влияние, заставляя упорядочивать инструкции в методе, в котором происходит присвоение.Создайте отдельный объект (private final Object X_LOCK = new Object();) и синхронизироваться с ним при установке или получении значения этого двойного значения.Это даст вам хороший уровень контроля над блокировкой, который, похоже, вам нужен.

В новом пакете параллелизма появилось больше опций, например AtomicReference, который может стать хорошей заменой Volatible, если вам действительно нужно избежать синхронизации.

Другие советы

Что ты пытаешься сделать?Тот Самый synchronized и volatile ключевые слова - это механизмы в Java, которые могут быть использованы для обеспечения того, чтобы разные потоки, считывающие одни и те же данные, наблюдали за согласованными значениями.В частности, они позволяют вам рассуждать о случается - до того, как отношения в ваших программах.

Вы просто не можете избежать использования одного из volatile или synchronized для того, чтобы правильно получить доступ к не-final поля в многопоточной программе.Тем не менее, основная причина, по которой вам, вероятно, потребуется synchronized закончился volatile является ли обязательным использование atomic сравните и установите операции (т.е. это не будет иметь никакого отношения к производительности).Например, в многопоточной программе:

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 это не проблема (в подавляющем большинстве случаев).Проблемы с производительностью синхронизации обычно возникают из-за ненужных узких мест кода, взаимоблокировок или блокировок в режиме реального времени и могут быть устранены при необходимости.Любой чистые тактовые циклы накладные расходы будут незначительны по сравнению с другими вещами, которые выполняет ваше приложение:ввод-вывод файлов, запросы к базе данных, удаленное взаимодействие и т.д.

Летучий, безусловно, подойдет, если вы выполняете только задание.Я уверен, что вы знаете, но раз уж это поднялось:если вы хотите выполнить более сложные операции (например, увеличить значение), вам потребуется синхронизировать.i++ никогда не является потокобезопасным для переменных любого типа.Вам нужно синхронизироваться.i++ и тому подобное, поскольку на самом деле это больше, чем одна операция.

Нет:Было сказано, что вы можете использовать AtomicDouble, но в настоящее время в java.util.concurrent.atomic нет AtomicDouble.

Если вы выполняете несколько операций над 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.

KDSRAthore, вы можете использовать некоторые явные блокировки или создать фиктивный Object object = new Object() , на котором вы синхронизируете метод установки/получателя этого двойного значения.

В соответствии с документация оракула, вы можете использовать Летучий для ссылки на объект Double:

volatile Double x = y;

«Запись и чтение ссылок всегда являются атомарными, независимо от того, реализованы ли они как 32-битные или 64-битные значения».

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top