публикация объектов и безопасность потоков - PullRequest
6 голосов
/ 26 февраля 2012

Я прочитал в "Java Concurrency In Practice", что "публикация объектов до того, как они полностью построены, может поставить под угрозу безопасность потоков". Может ли кто-нибудь объяснить это?

Ответы [ 2 ]

3 голосов
/ 26 февраля 2012

Рассмотрим этот код:

public class World{
    public static Point _point;

    public static void main(String[] args){
        new PointMaker().start();
        System.out.println(_point);
    }
}

public class Point{
    private final int _x, _y;

    public Point(int x, int y){
        _x = x;
        World._point = this;//BAD: publish myself before I'm fully constructed
        //some long computation here
        _y = y;
    }

    public void toString(){
        return _x + "," + _y;
    }
}

public class PointMaker extends Thread{
    public void run(){
        new Point(1, 1);
    }
}

Поскольку Point публикует себя перед установкой значения _y, вызов println может дать "1,0" вместо ожидаемого "1,1".

(Обратите внимание, что это также может привести к "null", если PointMaker + Point.<init> не зайдет достаточно далеко, чтобы установить поле World._point до выполнения вызова println.)

1 голос
/ 26 февраля 2012

Оператору new разрешено возвращать значение до завершения работы конструктора класса. Таким образом, переменная может не читать ноль, но содержит неинициализированный экземпляр класса. Это происходит из-за переупорядочения байтов.

Некоторые уточнения: С точки зрения одного потока JVM разрешено переупорядочивать некоторые инструкции. Традиционно создавая экземпляр, можно подумать, что он выглядит так:

  • выделить память
  • выполнить инициализацию (конструктор)
  • назначить ссылку на вар

Хотя на самом деле JVM может делать что-то вроде:

  • выделить память
  • назначить ссылку на var
  • запустить инициализацию (Конструктор)

Это имеет преимущества в производительности, так как адреса не нужно искать снова. С точки зрения одного потока это не меняет порядок логики. Ваша программа работает отлично. Но это создает проблему в многопоточном коде. Это означает, что ссылка может быть опубликована до запуска конструктора. Для этого вам необходимо правило «происходит до», чтобы убедиться, что экземпляр полностью инициализирован. Объявление переменных volatile dos обеспечивает соблюдение правил «произойдет раньше».

Подробнее о переупорядочении: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#reordering

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...