безопасная публикация изменяемого объекта - PullRequest
0 голосов
/ 24 мая 2018

Я прочитал несколько связанных вопросов, но ни один из них не объясняет способы безопасной публикации Держателя.Я все еще запутался в примере из Java Concurrency in Practice, раздел 3.5:

. Существует класс Holder:

public Holder {
    private int n;
    public Holder(int n) { this.n = n };
    public void assertSanity() {
        if(n != n)
             throw new AssertionError("This statement is false.");
    }
}

и его небезопасная публикация:

//unsafe publication
public Holder holder;
    public void initialize() {
        holder = new Holder(42);
    }

Ошибка AssertionError может быть выдана, и я согласен.Авторы пишут, что это из-за небезопасной публикации, но с другой стороны нет ответа: каков будет правильный способ публикации? Они указывают на 4 безопасных идиомы публикации, но я не понимаю, почемубудут ли они работать в приведенном выше случае:

Чтобы безопасно опубликовать объект, и ссылка на объект, и состояние объекта должны быть видны другим потокам одновременно.Правильно сконструированный объект можно безопасно опубликовать с помощью:

  1. Инициализация ссылки на объект из статического инициализатора;
  2. Сохранение ссылки на него в энергозависимом поле или AtomicReference;
  3. Сохранение ссылки на него в конечном поле правильно построенного объекта;
  4. или Сохранение ссылки на него в поле, которое надлежащим образом защищено замком.

Я согласен с 1 и 4, но сомневаюсь, почему следующие публикации будут работать:

//safe publication
public volatile Holder holder;

или

//safe publication
public final Holder holder;

volatile & final влияют только на справку, а не надля ссылочного состояния объекта, так что Я думаю, что ошибка AssertionError все еще возможна, верно?

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

private final int n;

Мне интересно, будет ли работать следующее?Как это связано с (эффективной) неизменностью?

private volatile int n;

Это мой первый вопрос, спасибо за вашу помощь!

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

На самом деле я думаю, что volatile здесь проще всего объяснить. Un безопасная публикация происходит, когда операции могут быть переупорядочены , а volatile предотвращает это.Я мог бы объяснить более вероятно, но это уже объяснено гораздо точнее, чем я буду .

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

final также делает вещи безопасными, и это конкретно записано в JLS .

Но здесь есть два случая в соответствии с: Хранение ссылки на негов окончательное поле правильно построенного объекта .

Итак, согласно JLS, это безопасная публикация:

class Holder {
    private final int n; // making final here
}

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

А как насчет этого примера?

static class Holder {

   private int n;

   public void setN(int n){
      this.n = n;
   }
}

И еще где-то:

 class Other {
    final Holder holder;
    Other(){
        holder = new Holder();
        holder.setN(12);
    }
 }

Это выглядиткак это все еще безопасная публикация в соответствии с это

0 голосов
/ 24 мая 2018

Сделайте целое число volatile и синхронизируйте его с блокирующим объектом с потоком, с которым вы согласны.

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

Class1:

public static final Object lock = new Object();
private Holder holder;
public abstract void method1(); //Assume these two go to different places
public abstract void method2(); //At different times w/ different implementations

Thread1:

public void method1() {
    synchronized(Class1.lock) {
        holder.assertMadness();
    }
}

Thread2:

public void method2() {
    synchronized(Class1.lock) {
        holder.assertMadness();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...