Java: достаточно изменчив, чтобы сделать классы безопасными для потоков? - PullRequest
3 голосов
/ 16 декабря 2011

У меня есть вопрос об изменчивом утверждении в Java. Пожалуйста, посмотрите на этот построенный пример:

class Master {
    // Foo is a class with thread-safe methods
    public volatile Foo foo;
}

class Worker1 implements Runnable {
    protected Master master

    void run() { ... };
}

class Worker2 implements Runnable {
    protected Master master

    void run() { ... };
}

У нас есть 2 рабочих потока, которые содержат ссылку на объект класса Master, запущенный одновременно. Во время своей работы оба должны иметь доступ к методам master.foo. В какой-то момент объект Foo мастера будет изменен одним из рабочих потоков. Теперь мой вопрос: делает ли использование volatile всю конструкцию поточно-ориентированной? Я спрашиваю это, потому что в руководстве по Java от Oracle написано

Однако есть действия, которые можно указать как атомарные:

  • Чтение и запись являются атомарными для ссылочных переменных [...]

Атомарные действия не могут чередоваться, поэтому они могут использоваться без опасения вмешательства потока.

Я просто хотел бы убедиться, что правильно понял это.

Заранее спасибо:)

Ответы [ 4 ]

11 голосов
/ 16 декабря 2011

Чтение и запись ссылок всегда являются атомарными в Java, поэтому нет опасности, например, увидеть ссылку в каком-то наполовину обновленном состоянии. Если это то, что вы подразумеваете под «потокобезопасность», то операция является поточно-безопасной с ключевым словом или без него.

Но volatile не относится к атомарности, он влияет на то, могут ли потоки кэшировать записи локально. volatile приведет к тому, что записи будут записаны обратно в основную память и будут видны другим потокам. Если это то, что вы подразумеваете под потокобезопасностью, тогда да, вам нужно это ключевое слово.

volatile само по себе не влияет на то, исключают ли вызовы методов на foo другие потоки или нет. Если это то, что вы имеете в виду, вы хотите использовать synchronized, и в другом месте.

4 голосов
/ 16 декабря 2011

volatile гарантирует, что поток, читающий переменную, увидит новое значение, установленное в этой переменной ранее другим потоком.Он не увидит устаревшее значение, хранящееся в кэше процессора.

От того, делает ли программа всю программу безопасной для потоков, зависит от того, что делает программа, как она использует переменную и как разработан класс Foo.

3 голосов
/ 16 декабря 2011

летучих недостаточно.

Я полагаю, что этот пример демонстрирует одну ситуацию, когда энергозависимости недостаточно:

Общий объект:

public class Blammo
{
  public String kapow;
}

Код в потоке 1:

Blammo blammo = new Blammo();
blammo.kapow = "zowie";
if ((blammo.kapow != null) && (blammo.kapow.isEmpty()))
{
  ...
}

Код в потоке 2:

blammo.kapow = null;

Поток 1 выполняется и выполняется (blammo.kapow! = Null).
Нить 1 обменивается, а нить 2 обменивается.
Выполняется поток 2 (blammo.kapow = null).
Нить 2 заменяется, а нить 1 включается.
Поток 1 теперь выполняется (blammo.kapow.isEmpty ()) и выдает исключение нулевого указателя.

1 голос
/ 16 декабря 2011

Если вы только устанавливаете эту переменную, тогда да, достаточно volatile.Как указано в комментариях, вам нужно volatile, чтобы сделать изменения видимыми (однако у вас не будет никаких поврежденных обновлений без volatile, как вы могли бы иметь с long или double, см. Ниже).

По умолчаниючтение и запись ссылочных переменных (ссылок на объекты) являются атомарными, как и чтение и запись для большинства примитивных типов (кроме long и double).

С помощью volatile вы можете убедиться, что чтение и запись в переменныеТип long и double также являются атомарными, что в данном случае вам не нужно.

Так что, да, в этом примере этого достаточно.Однако, если вы планируете отправлять более сложные сообщения, рассмотрите возможность использования инструментов из java.util.concurrent. *

...