Предотвращает ли создание поля 'volatile' все проблемы видимости памяти в параллельной ситуации? - PullRequest
6 голосов
/ 07 января 2012

Предотвращает ли создание поля класса volatile всех проблем с видимостью памяти в параллельной ситуации? Возможно ли, что для класса ниже поток, который получает ссылку на объект Test, сначала видит x как 0 (значение по умолчанию int), а затем 10? Я думаю, что это возможно тогда и только тогда, когда Конструктор Test отдает ссылку this без завершения (неправильная публикация). Может ли кто-нибудь подтвердить / исправить меня?

class Test {
    volatile int x = 10;            
}

Второй вопрос: что, если это был final int x=10;?

Ответы [ 2 ]

6 голосов
/ 07 января 2012

На самом деле вам не гарантировано видеть x = 10 в соответствии с JMM.

Например, если у вас есть

Test test =  null;

Thread 1 -> test = new Test();
Thread 2 -> test.x  == // even though test != null, x can be seen as 0 if the
                       // write of x hasn't yet occur

Теперь, если у вас было

class Test{
  int y = 3;
  volatile x = 10;
}

Если поток-2 читает x == 10, поток-2 гарантированно читает y == 3

Чтобы ответить на ваш второй вопрос.

Наличие последнего поля приведет к созданию storetore после конструктора и перед публикацией, поэтому наличие поля final фактически гарантирует, что вы увидите x = 10.

Редактировать: Как заметил ишавит. Вы теряете отношение «происходит до», о котором я упоминал в моем первом примере с последними полями, то есть, как выразился yshavit, если thread-2 читает x == 10, он может не прочитать y == 3, где x - конечное поле.

3 голосов
/ 07 января 2012

Даже в однопоточной реализации вы не гарантированно увидите x = 10, если вы пропустите this в конструкторе. Таким образом, проблема, с которой вы можете столкнуться, это не проблема параллелизма, а проблема порядка выполнения (в зависимости от того, когда вы пропустите this ). Например. если вы пропустите this в родительском конструкторе для instace:

public class TestParent
{
  public TestParent()
  {
    if (this instanceof TestChild)
    {
      TestChild child = (TestChild) this;
      System.out.println(child.field);  // will print 0 when TestChild is instantiated.
    }
  }
}


public class TestChild extends TestParent
{
  volatile int field = 10;
}

public static void main(String[] args)
{
  TestChild child = new TestChild();
  System.out.println(child.field);

  // The above results in 0 (from TestParent constructor) then 10 being printed.
}

Конечные поля, с другой стороны, гарантированно будут иметь назначенное начальное значение, если это присваивание выполняется в объявленной строке (если вы сделаете поле финальным, но инициализируете его в конструкторе, вы все равно можете просочиться this before и покажите неинициализированное значение.

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