2013-06-06 2 views
9

У меня возникли трудности с пониманием изменчивых переменных в Java.Нужны ли переменные волатильности для синхронного доступа?

У меня есть параметризованный класс, который содержит летучее переменную как так:

public class MyClass<T> { 

    private volatile T lastValue; 

    // ... other code ... 

} 

я должен реализовать некоторые основные операции против lastValue, включающих в себя получить значение, если-не-нуль.

Нужно ли синхронизировать эти операции? Могу ли я уйти с выполнением следующего метода?

public void doSomething() { 
    String someString; 
    ... 
    if (lastValue != null) { 
     someString += lastValue.toString(); 
    } 
} 

Или мне нужно вставить нулевую проверку в синхронизированный блок?

public void doSomething() { 
    String someString; 
    ... 

    synchronized(this) { 
     if (lastValue != null) { 
      someString += lastValue.toString(); 
     } 
    } 
} 

Я знаю, что для атомарных операций, как получить и установить, я должен быть в порядке, без применения синхронизации (например. public T getValue() { return lastValue; }). Но я не был уверен в неатомных операциях.

+0

Переписывая 'SomeString + = lastValue.toStrin()' как 'SomeString + = ("" + LastValue);' позволяет избавиться от 'null' проверки в целом. – dasblinkenlight

+4

@dasblinkenlight, true, но только если вы в порядке со словом «null», отображаемым в вашей результирующей строке, когда lastValue имеет значение NULL. –

+0

@IanMcLaird Ах, вы правы, я забыл, что Java делает это (это .NET, который этого не делает). – dasblinkenlight

ответ

13

volatile гарантирует видимость (изменения, сделанные одной нитью, будут видны другими темами), но это не гарантирует атомарность нескольких операций.

Так что да, lastValue может стать null между if (lastValue != null) и someString += lastValue.toString(); и ваш код может бросить NullPointerException.

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

public void doSomething() { 
    String someString; 
    T lastValueCopy = lastValue; 
    if (lastValueCopy != null) { 
     someString += lastValueCopy.toString(); 
    } 
} 
+0

Почему добавление синхронизации к нулевой проверке требует добавления синхронизации к атомным операциям против изменчивой переменной? –

+0

Правильно ли я понимаю, что нет понятий «до» и «после» для потоков, кроме случаев, когда они синхронизируются в какой-то момент времени? Семантика потока позволяет потоку видеть старое значение переменной после того, как другой поток обновил его? – kutschkem

+0

@ RoddyoftheFrozenPeas Вы правы, вам нужно всего лишь синхронизировать все операции записи. Это необходимо, потому что здесь используется 'synchronized' для взаимного исключения, т. Е. Чтобы ни одна нить не изменяла вашу переменную между этими двумя строками. Это может работать, только если все потоки, пытающиеся записать в переменную, должны получить эту блокировку. – assylias

2

Это в значительной степени зависит от свойства типа T. В простейшем случае T является неизменным типом, то есть любое значение T, которое вы читаете, впоследствии не изменит его внутреннего состояния. В этом случае вам не нужна синхронизация, просто прочитайте ссылку в локальной переменной и поработайте с ней, пока вам нужно.

Если T является изменяемым типом, вам нужна защита от экземпляра, изменяющего его состояние во время работы с ним. В этом случае вам нужна синхронизация, которая специально гарантирует, что экземпляр T защищен им. Синхронизация по обычно не гарантирует, что это не помешает вам изменить состояние T из любого другого места. Правильная блокировка для определенного Т должна определяться правилами (и нет языкового элемента, гарантирующего, что вы не нарушаете эти правила).

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

Это говорит о том, что использование летучих в соединении с синхронизированным выглядит подозрительно.

+0

@loki Не обязательно, но когда он станет видимым, он будет в согласованном состоянии. Если объект не является неизменным, когда он становится видимым, поток может видеть объект с некоторыми правильно построенными полями, а некоторые поля имеют свои значение по умолчанию (false, 0, null). – assylias

0

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

Более подробную информацию о: http://oracle2java.blogspot.com.br/2013/12/java-sincronizar-referencia-entre.html

Смежные вопросы