Простое определение «инварианта»: условие, которое всегда верно в течение времени жизни объекта .
Изменчивые переменные не имеют общих свойств synchronized
blocks.
Вот почему вы не можете использовать их в классе, имеющем инварианты, которые связаны с несколькими переменными.
Например, представьте, что у вас есть 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
}
В этом случае вы можете быть уверены, что изменение видимо для каждого потока, но в середине двух инструкций состояние объекта может быть недопустимым.Поскольку к нему могут обращаться другие потоки (помните, что это простой случай, поэтому он возможен, но маловероятен), тогда условие инварианта «начало <конец» может быть нарушено. </p>
Именно поэтому использование 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 , он предоставляет несколько простых базовых процедур для параллельного программирования без блокировок.