Как понять, что переменная не участвует в инвариантах с другими переменными состояния при использовании ключевого слова volatile?

Из книги "Параллелизм Java на практике", стр. 26:

Вы можете использовать изменчивые переменные, только если соблюдены все следующие критерии:

  • Запись в переменную не зависит от ее текущего значения, или вы можете гарантировать, что только один поток всегда обновляет значение;

  • Переменная не участвует в инвариантах с другими переменными состояния; и

  • Блокировка не требуется по какой-либо другой причине во время доступа к переменной.

Как понимать «переменная не участвует в инвариантах с другими переменными состояния при использовании ключевого слова volatile»?


person xing.zhang    schedule 26.03.2012    source источник
comment
Еще немного контекста было бы полезно, иначе я бы просто догадывался, что это может означать.   -  person Peter Lawrey    schedule 26.03.2012
comment
Извините, я добавил контекст сейчас   -  person xing.zhang    schedule 26.03.2012
comment
В моей книге это 39 страниц   -  person gstackoverflow    schedule 09.02.2017


Ответы (1)


Простое определение «инварианта»: условие, которое всегда истинно в течение всего времени существования объекта.

Изменчивые переменные не обладают атомарными свойствами блоков synchronized.

Вот почему вы не можете использовать их в классе, который имеет инварианты, связывающие несколько переменных.

Например, представьте, что у вас есть class для моделирования временного интервала, описываемого двумя переменными: start и end. Неизменным условием может быть то, что start всегда меньше или равно end. Если обе переменные (просто в качестве примера) объявлены как volatile, то вы можете полагаться на функции видимости volatile, но вы не можете быть уверены, что во время изменения, включающего обе переменные, инвариант всегда выполняется. Считать:

public void setInterval(Date newStart, Date newEnd)
{
 // Check if inputs are correct

 // Here the object state is valid
 start = newStart;

 // If another thread accesses this object now it will
 // see an invalid state because start could be greater than end

 end = newEnd;
 // Here the object state is valid again
}

В этом случае вы можете быть уверены, что изменение видно каждому потоку, но в середине двух инструкций состояние объекта может быть недействительным. Поскольку к нему могут обращаться другие потоки (помните, что это простой случай, так что это возможно, но маловероятно), то инвариантное условие «начало ‹ конец» может быть нарушено.

Вот почему использование volatile почему-то не рекомендуется за пределами (небольшого) набора четко определенных шаблонов. Волатильную переменную следует использовать только в том случае, если выполняются следующие условия:

  • Переменная не участвует в инвариантах, связанных с другими переменными (по причине, объясненной выше).
  • Значение, записываемое в переменную, не зависит от ее текущего значения.

Например, выражение int a = i++; не является атомарным, тогда оно, строго говоря, не потокобезопасно, потому что оно будет переписано примерно так:

int temp = i;
i = i + 1;
int a = temp;

Чтобы сделать его атомарным с точки зрения потока, вы можете представить себе такой класс:

public class MyAtomicInteger
{
  public synchronized increment()
  {
    x = x + 1;
  }

  private int x;
}

Конечно, существует настоящая реализация этого AtomicInteger, и она является частью пакета java.util.concurrent.atomic, он предоставляет несколько простых базовых подпрограмм для параллельного программирования без блокировок.

person Adriano Repetti    schedule 26.03.2012
comment
Есть ошибка: это не i = i + 1, а i = temp + 1 в вашем примере приращения i, иначе это приведет к бесконечным подоператорам, поскольку i = i + 1 совпадает с операцией i++. - person Mik378; 15.11.2012
comment
@Mik378 перед приращением i == temp, поэтому i = i + 1 эквивалентно i = temp + 1. Это то, как ++ расширяется, а не +, тогда i + 1 больше не будет расширяться - person Adriano Repetti; 15.11.2012