Не ориентированная на многопотоковое исполнение Объектная публикация

Купите себя Калькулятор HP 10C , и затем программа все те математические алгоритмы "программирования 101" с помощью его безумно маленького, но практического языка калькулятора. Напоминает мне об ассемблере, но это не.

14
задан Matthias Braun 23 April 2019 в 13:06
поделиться

5 ответов

Причина, по которой это возможно, состоит в том, что Java имеет слабую модель памяти. Это не гарантирует упорядочение чтения и записи.

Эта конкретная проблема может быть воспроизведена с помощью следующих двух фрагментов кода, представляющих два потока.

Поток 1:

someStaticVariable = new Holder(42);

Поток 2:

someStaticVariable.assertSanity(); // can throw

На первый взгляд это кажется невозможным что это могло когда-либо произойти. Чтобы понять, почему это может произойти, вам нужно обойти синтаксис Java и перейти на гораздо более низкий уровень. Если вы посмотрите на код для потока 1, его можно по существу разбить на серию операций записи и выделения памяти:

  1. Выделить память для указателя1
  2. Записать 42 в указатель1 со смещением 0
  3. Записать указатель1 в someStaticVariable

Поскольку Java имеет слабую модель памяти, вполне возможно, что код действительно будет выполняться в следующем порядке с точки зрения потока 2:

  1. Выделить память для указателя1
  2. Записать указатель1 в someStaticVariable
  3. Записать 42 в указатель1 со смещением 0

Страшно? Да, но это может случиться.

Однако это означает, что поток 2 теперь может вызывать assertSanity до того, как n получит значение 42. Это возможно для значения ] n должен быть прочитан дважды во время assertSanity , один раз перед завершением операции № 3 и один раз после, и, следовательно, увидеть два разных значения и выбросить исключение.

РЕДАКТИРОВАТЬ

Согласно Джону Скиту , ошибка AssertionError может все еще возникать с Java 8 , если поле не является окончательным.

14
ответ дан 1 December 2019 в 12:26
поделиться

Модель памяти Java использовала для того, чтобы присвоение ссылке Держатель могло стать видимым до присвоения переменной внутри объекта.

Однако более новая модель памяти, которая вступила в силу в Java 5, делает это невозможным, по крайней мере, для полей final: все присвоения в конструкторе «происходят до» любого присвоения ссылки на новый объект переменной. См. раздел 17.4 Спецификации языка Java для получения более подробной информации, но вот наиболее подходящий фрагмент:

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

Таким образом, ваш пример все равно может завершиться неудачно, поскольку n не является окончательным, но все будет в порядке, если вы сделаете n окончательным.

Конечно:

if (n != n)

определенно может завершиться ошибкой для не конечных переменных, если JIT-компилятор не оптимизирует их - если операции следующие:

  • Fetch LHS: n
  • Fetch RHS: n
  • Сравнить LHS и RHS

, тогда значение может измениться между двумя выборками.

11
ответ дан 1 December 2019 в 12:26
поделиться

Что ж, в книге для первого блока кода говорится, что:

Проблема здесь не в держателе сам класс, но Держатель не опубликовано должным образом. Однако, Держатель можно сделать невосприимчивым к неправильному публикация путем объявления поля n быть окончательным, что сделало бы Держатель неизменный; см. Раздел 3.5.2

И для второго блока кода:

Поскольку синхронизация не использовалась сделать Держателя видимым для других темы, мы говорим, что Держатель не был правильно опубликовано. Две вещи могут пойти неправильно с неправильно опубликованным объекты. Другие темы могли видеть устаревшее значение для поля держателя и таким образом, смотрите пустую ссылку или другой более старое значение, даже если значение имеет помещен в держатель. Но гораздо хуже, другие потоки могли видеть последнюю значение для ссылки держателя, но устаревшие значения для состояния Держатель. [16] Чтобы сделать еще меньше предсказуемо, поток может увидеть устаревший значение при первом чтении поля а затем более актуальное значение в следующий раз, поэтому assertSanity может выбросить AssertionError.

Я думаю, что ДжаредПар в значительной степени сделал это явным в своем комментарии.

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

1
ответ дан 1 December 2019 в 12:26
поделиться

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

a = 1;
b = 2;

Если вы сделаете это в одном потоке, второй поток может увидеть, что b установлено в 2, прежде чем a будет установлено в 1. Кроме того, может быть неограниченное количество времени между тем, как второй поток увидит один. из этих переменных обновляются, а другая переменная обновляется.

0
ответ дан 1 December 2019 в 12:26
поделиться

если посмотреть на это с разумной точки зрения, если вы предположите, что утверждение

if (n! = N)

является атомарным (что я считаю разумным, но я не знаю наверняка), то исключение утверждения никогда не может быть сгенерировано.

-1
ответ дан 1 December 2019 в 12:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: