Окончательная семантика и десериализация поля с помощью setAccessible (true) - PullRequest
9 голосов
/ 05 октября 2011

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

JLS говорит о 17.5.3 Последующая модификация конечных полей и смутно утверждает, что

Реализация может обеспечить способ выполнения блока кода в окончательном безопасном контексте поля .

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

Я должен сказать, что не полностью понял частичные порядки разыменования () и mc () , а также поведение действия freeze , которое выполняет ставить после любой модификации последнее поле (либо приписанное ему начальное значение, либо последующие модификации).

В этом контексте я хочу знать следующее: как может (de) инфраструктура сериализации, такая как Gson, гарантировать, что десериализованные объекты, содержащие конечные поля, должным образом инициализированные в конструкторе, не создадут проблему видимости потока?

Например, рассмотрим этот класс:

class X {
  private final String s;
  public X(final String s) { this.s = s; }
  @Override public String toString() { return s; }
}

и следующий код:

final Gson gson = new Gson();
X x = gson.fromJson(gson.toJson(new X("abc")), X.class);
System.out.println(x);
// prints abc

Войдя в метод fromJson с помощью отладчика, я вижу, что sun.misc.Unsafe используется для выделения экземпляра X без вызова его конструктора, и поля равны setAccessible(true), и, наконец, они устанавливаются.

И это только в Sun (или совместимых) JVM! Похоже, у Gson есть код, специфичный для нескольких версий Android.

Итак, существуют ли какие-либо гарантии безопасности потока, связанные с этими десериализованными конечными полями , точно так же, как я бы сделал с экземпляром X, созданным с new X("abc")? Если да, то откуда берется эта гарантия?

Спасибо!

Ответы [ 3 ]

1 голос
/ 05 октября 2011

Безопасность потока

Насколько я понимаю, гарантия безопасности потока основана на том факте, что данный атрибут объявлен как final. Точка, в которой это НЕ является потокобезопасным, является либо:

  • Во время десериализации , когда выделяется пространство памяти объекта, но перед атрибутом final присваивается значение
  • Во время изменения поля final через Reflection API (т. Е. Когда значение находится в процессе изменения и до того, как это будет сделано с помощью этого процесса)

Предостережение в том, что ссылка, на которую вы ссылаетесь, допускает теоретическую возможность чего-то другого , кроме Reflection API (но с теми же final возможностями модификации поля).

Замораживание

Замораживание конечного поля происходит как в конце конструктора в какое последнее поле установлено, и сразу после каждой модификации конечного поля с помощью отражения или другого специального механизма.

Что касается заморозки вызовов, то в основном говорится, что freeze используется для пометки атрибутов как «это нельзя изменить», и это так:

  • В конце выполнения конструктора, в котором неопределенному полю final фактически присваивается значение
  • После изменения значения поля final через что-то вроде Reflection API

Потокобезопасность применяется только к изменяемому / десериализованному объекту.

Итак:

(...code that comes before...)
END final field safe context - entering non-threadsafe area

  final fields are not frozen
    code that deserializes an object OR modifies a final field via reflection
  final fields are re-frozen

RESUME final field safe context - re-entering threadsafe area
(...code that comes after...)

Поэтому ...

После запуска конструктора (т. Е. У вас есть экземпляр объекта с назначенными значениями), окончательное поле не может быть изменено, потому что оно заморожено, за исключением случая, когда API Reflection используется для непосредственного изменения этого значения - после который снова заморожен, потому что, в конце концов, поле объявлено как окончательное.

Если вы ищете специфичный для Android ответ на этот вопрос (чтобы убедиться, что его поведение JVM согласуется с JVM от Sun / Oracle), вам необходимо найти эквивалентную документацию для этой JVM.

1 голос
/ 05 июля 2012

Размораживание происходит вместе с вызовом (java.reflect.) Field.setAccessible (true).Большинство фреймворков, регулярно использующих отражение для установки конечных полей, часто никогда не вызывают field.setAccessible (false) после успешной модификации, поэтому оставляя это поле «незамерзшим».

Таким образом, любой другой ответственный фреймворк увидит, что поле доступнои мог бы сделать это.Это теоретический шанс, но это может произойти.Существует причина, по которой такой тип операций внутри механизма сериализации использует метод класса Unsafe (пакет реализации sun).

Так что при использовании любого вида API отражения вы действительно можете испортить JVM ивызвать любое непредсказуемое поведение.Единственное, что сохраняет механизм десериализации по умолчанию, это то, что он выполняется во время создания экземпляра, где нет никаких шансов, что будет какой-либо одновременный доступ.

Вот почему такой доступ может быть ограничен или даже запрещенSecurityManager.

0 голосов
/ 05 октября 2011

Из JLS

Замораживание конечного поля происходит как в конце конструктора, в котором установлено конечное поле, так и сразу после каждой модификации конечного поля с помощью отражения или другого специального механизма..

, поскольку эффект памяти определяется только в виде действия freeze, это означает, что семантика также применяется, если поле final изменяется после конструктора - до тех пор, пока ссылка на объект не являетсядо этого просочилась.Это считается допустимым вариантом использования.

Как только ссылка на объект опубликована, дальнейшая модификация в последнем поле не является хорошей идеей.

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