2010-06-03 2 views
14

Скажем, у меня есть два потока и объект. Один потока объект:Как правильно использовать ключевое слово volatile в Java?

public void assign(MyObject o) { 
    myObject = o; 
} 

Другой поток использует объект:

public void use() { 
    myObject.use(); 
} 

ли должна быть объявлена ​​переменная MyObject летучими? Я пытаюсь понять, когда использовать volatile, а когда нет, и это меня озадачивает. Возможно ли, что второй поток содержит ссылку на старый объект в своем кеше локальной памяти? Если нет, почему бы и нет?

Большое спасибо.

+1

Какую версию Java вы используете? – Alerty

+0

Я развиваюсь на мобильном устройстве. Это в основном java 1.4.x – Tiyoal

ответ

6

Оставив сложные технические детали позади, вы можете увидеть volatile меньше или больше в качестве модификатора synchronized для переменных. Когда вы хотите, чтобы синхронизировать доступ к методам или блоков, то вы обычно хотите использовать модификатор synchronized следующим образом:

public synchronized void doSomething() {} 

Если вы хотите «синхронизировать» доступ к переменным, то вам» хотел бы использовать volatile модификатор:

private volatile SomeObject variable; 

за кулисами они делают разные вещи, но эффект одно и то же: изменения сразу видны для следующего за доступом нити.

В вашем конкретном случае я не думаю, что модификатор volatile имеет любое значение. volatile не гарантирует, что поток, назначающий объект, будет запускать до поток с использованием объекта. Это может быть так же хорошо. Вероятно, вы просто хотите сначала выполнить nullcheck в методе use().

Обновление: также увидеть this article:

Доступ к переменной действует как будто он заключен в синхронизированный блок, синхронизированы на себя. Мы говорим, что «действует как бы» во втором пункте, потому что для программиста, по крайней мере (и, вероятно, в большинстве реализаций JVM) , фактический объект блокировки не задействован.

+7

Это абсолютно неверно. Volatile полностью касается эффектов памяти и видимости переменной, а не одновременного доступа. – Kevin

+0

Я смущен. Я читал, и я читал, что volatile обычно используется для обмена флагом между потоками: один поток устанавливает поток в false, а остальные уведомления, которые были установлены в false. Если логическое значение не объявлено как изменчивое, второй поток всегда будет замечать, что было установлено значение false, потому что значение могло быть сохранено в кэше локального потока. Разве это не так, или я неправильно понял это утверждение? – Tiyoal

+3

@ Кевин: Да, это техническая деталь. Я говорил о ** эффекте **, так что это имеет смысл для начинающих. Изменение переменной сразу видно для следующего потока доступа. – BalusC

11

Я пытаюсь понять, когда использовать летучий и когда не

Вы должны в основном избежать его использования. Вместо этого используйте AtomicReference (или другой класс atomic, где это необходимо). Эффекты памяти одинаковы, а целью является много clearer.

Я настоятельно рекомендую прочитать отличный Java Concurrency in Practice для лучшего понимания.

+2

Спасибо за совет. Я согласен, что мне не нужно использовать эти «низкоуровневые вещи». Тем не менее, я чувствую желание понять, как это работает под капотом. Книга - очень хорошее предложение для этого. Благодарю. – Tiyoal

+0

@Tiyoal Для абсолютной мелочи, см. Раздел 17.4 JLS, в котором обсуждается модель памяти: http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4 – Kevin

+0

Во-вторых, параллелизм на практике. Volatile - более слабая форма синхронизации ... нормально использовать, если переменная записывается одним потоком и считывается многими потоками, чтобы гарантировать, что записи будут видны сразу. – bwawok

2

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

+0

Но как это позаботится о том, чтобы поток, назначающий объект, запускал * перед * поток, используя объект? – BalusC

+0

Зачем нужна синхронизация здесь? Является ли объявление волатильным недостаточно? – Tiyoal

+0

@Tiyoal - это либо/или. Вам нужно использовать один из этих вариантов. – Robin

0

Есть некоторые запутанные комментарии здесь: уточнить, что ваш код неправильно, как он стоит, предполагая, что два различных поток вызывает assign() и use().

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

Да, volatile - это один из способов исправить это (если это неправильное поведение - возможны ситуации, когда это вас не волнует!).

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

4

Декларирование летучий переменную Java означает:

  • Значение этой переменной не будет кэшировать поток локально
  • Доступ к переменной действует как будто она заключена в синхронизированный блок

типичным и наиболее распространенный способ использования летучих является:

public class StoppableThread extends Thread { 
    private volatile boolean stop = false; 

    public void run() { 
    while (!stop) { 
     // do work 
    } 
    } 

    public void stopWork() { 
    stop = true; 
    } 
} 
1

Я потратил немало времени, пытаясь понять ключевое слово volatile. Я думаю, @aleroot дал лучший и самый простой пример в мире.

Это в свою очередь, мое объяснение для чайников (как я :-)):

Scenario1: Если предположить, что stop не объявлена ​​как летучий то данный поток делает и «думает» следующее:

  1. stopWork() называется: Я должен установить stop в true
  2. Отлично, я сделал это в моем местном стеке теперь я обновить основную кучу JVM.
  3. Упс, JVM говорит мне дать путь в CPU к другому потоку, я должен остановиться на некоторое время ...
  4. Хорошо, я вернулся. Теперь я могу обновить основную кучу с моей стоимостью. Обновление ...

Scenario2: Пусть теперь stop быть объявлен как летучий:

  1. stopWork() называется: Я должен установить stop в true
  2. Отлично, я сделал это в моем местном стеке прямо сейчас Я должен обновить основную кучу JVM.
  3. Извините, ребята, я должен сделать (2) СЕЙЧАС - мне сказали, что это volatile. Я должен занять процессор немного дольше ...
  4. Обновление основной кучи ...
  5. ОК, я закончил. Теперь я могу уступить.

Нет синхронизации, только простая идея ...

Почему не объявлять все переменные volatile всякий случай? Из-за Scenario2/Step3. Это немного неэффективно, но все же лучше обычной синхронизации.

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