Конечная семантика и десериализация поля с setAccessible (true)

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

В JLS говорится о 17.5.3 Последующее изменение окончательной версии Поля и неопределенно заявляет, что

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

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

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

В этом контексте я хочу знать следующее: как может структура (де) сериализации, такая как 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), и, наконец, они устанавливаются.

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

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

Спасибо!


person Bruno Reis    schedule 04.10.2011    source источник


Ответы (3)


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

Я читал, что гарантия безопасности потоков исходит из того факта, что данный атрибут объявлен как 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.

person jefflunt    schedule 05.10.2011
comment
Как JVM узнает, когда поля повторно замораживаются после создания объекта? Т.е. где гарантия, что поток не прерывается между sun.misc.Unsafe#allocateInstance(Class) и установкой всех полей через отражение? Сколько раз final field safe context раздел остается и вводится во время десериализации объекта? - person dma_k; 25.11.2011
comment
JVM знает, когда что-то замораживается и размораживается, потому что это то, что делает замораживание / размораживание. Я не знаю никакой гарантии, что прерывание потока не может произойти в тот момент времени, о котором вы говорите. По моему опыту, потоки - это не технология, цель которой - получить подобные гарантии, однако более важно знать, какие ситуации и поведение кода могут вызвать с этим проблемы, и избегать такого поведения, а не искать обещания. из JVM. Многопоточность - сложная вещь, и она требует определенных компромиссов. - person jefflunt; 25.11.2011

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

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

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

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

person Martin Kersten    schedule 05.07.2012

От JLS

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

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

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

person irreputable    schedule 05.10.2011