Эффекты памяти синхронизации в Java

Во-первых: не теряйте TargetInvocationException - это - ценная информация, когда Вы захотите отладить вещи.
Второй: Оберните TIE как InnerException в Вашем собственном типе исключительной ситуации и поместите свойство OriginalException, которое связывается с тем, что Вы нуждаетесь (и сохраняете весь стек вызовов в целости).
Треть: Позвольте пузырю TIE из своего метода.

41
задан Binil Thomas 5 December 2009 в 01:26
поделиться

5 ответов

Короткий ответ заключается в том, что JSR-133 заходит слишком далеко в своем объяснении . Это не является серьезной проблемой, потому что JSR-133 - ненормативный документ, который не является частью языка или стандартов JVM. Скорее, это всего лишь документ, объясняющий одну возможную стратегию, которая достаточна для реализации модели памяти, но в целом не является необходимой . Вдобавок ко всему, комментарий о «очистке кеша» в основном неуместен, поскольку практически нулевые архитектуры реализуют модель памяти Java, выполняя любой тип «

Таким образом, цитируемый вами текст формально не описывает то, что гарантирует Java, а скорее описывает, как некая гипотетическая архитектура с очень слабым упорядочением памяти и гарантиями видимости могла удовлетворить требования модели памяти Java с использованием очистки кеша. . Любое фактическое обсуждение очистки кеша, основной памяти и т. Д. Явно неприменимо к Java, поскольку эти концепции не существуют в спецификации абстрактного языка и модели памяти.

На практике гарантии, предлагаемые моделью памяти, весьма значительны. слабее, чем полная очистка - сброс всего кеша с помощью каждой атомарной операции, связанной с параллелизмом или блокировкой будет непомерно дорогостоящим - а на практике это почти никогда не делается. Вместо этого используются специальные атомарные операции ЦП, иногда в сочетании с инструкциями барьера памяти , которые помогают обеспечить видимость и упорядоченность памяти. Таким образом, очевидное несоответствие между дешевой неконтролируемой синхронизацией и «полной очисткой кеша» разрешается за счет того, что первое верно, а второе - нет - модель памяти Java не требует полного сброса (и на практике сброса не происходит).

Если формальная модель памяти слишком сложна для переваривания (вы не будете одиноки), вы также можете глубже погрузиться в эту тему, взглянув на Поваренную книгу Дуга Ли , которая находится в Фактически, ссылка на него содержится в FAQ по JSR-133, но решает проблему с точки зрения конкретного оборудования, поскольку он предназначен для разработчиков компиляторов. Там они говорят о том, какие именно барьеры нужны для конкретных операций, включая синхронизацию - и обсуждаемые там барьеры довольно легко сопоставить с реальным оборудованием. Большая часть фактического отображения обсуждается прямо в кулинарной книге.

42
ответ дан 27 November 2019 в 00:42
поделиться

Обновлениям x требуется синхронизация, но делает ли приобретение замка очистить значение y также от кеш? Я не могу представить, что это будет случае, потому что если бы это было правдой, такие методы, как разделение замков, могут не поможет.

Я не уверен, но думаю, что ответ может быть «да». Учтите следующее:

class Foo {
    int x = 1;
    int y = 1;
    ..
    void bar() {
        synchronized (aLock) {
            x = x + 1;
        }
        y = y + 1;
    }
}

Теперь этот код небезопасен, в зависимости от того, что происходит в остальной части программы. Однако я думаю, что модель памяти означает, что значение y , видимое полосой , не должно быть старше «реального» значения на момент получения блокировки. Это означало бы, что кеш должен быть признан недействительным для y , а также для x .

Также JVM может надежно анализировать код, чтобы гарантировать, что y не изменяется в другом синхронизированном блоке, используя та же самая блокировка?

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

В более общих случаях проблема доказательства того, что данная блокировка когда-либо используется только в связи с данным экземпляром «владеющим», вероятно, является неразрешимой. .

7
ответ дан 27 November 2019 в 00:42
поделиться

мы Java-разработчики, мы знаем только виртуальные машины, а не настоящие!

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

скажем, поток A запущен на CPU A с кешем A, поток B работает на CPU B с кешем B,

  1. поток A читает y; CPU A извлекает y из основной памяти и сохраняет значение в кэше A.

  2. поток B присваивает новое значение «y». На этом этапе ВМ не нужно обновлять основную память; что касается потока B, он может читать / писать на локальном образе 'y'; возможно, «y» - не что иное, как регистр процессора.

  3. поток B выходит из блока синхронизации и освобождает монитор. (когда и где он вошел в блок, значения не имеет). к этому моменту поток B обновил довольно много переменных, включая «y». Все эти обновления теперь должны быть записаны в основную память.

  4. ЦП B записывает новое значение y, чтобы поместить «y» в основную память. (Я полагаю, что) почти МГНОВЕННО информация «main y обновлена» подключается к кешу A, а кеш A делает недействительной собственную копию y. Это должно было произойти действительно БЫСТРО на оборудовании.

  5. поток A получает монитор и входит в блок синхронизации - в этот момент ему не нужно ничего делать с кешем A. 'y' уже ушел из кеша A. когда поток A снова читает y, он только что из основной памяти с новым значением, присвоенным B.

рассмотрим другую переменную z, которая также была кэширована A на шаге (1), но она не обновлена ​​потоком B на шаге (2 ). он может сохраняться в кэше A до шага (5). доступ к 'z' не замедляется из-за синхронизации.

Если приведенные выше утверждения имеют смысл, тогда цена действительно не очень высока.


дополнение к шагу (5): поток A может иметь свой собственный кеш, который даже быстрее, чем кеш A - например, он может использовать регистр для переменной 'y'. это не будет аннулировано на шаге (4), поэтому на шаге (5) поток A должен стереть свой собственный кеш при входе в синхронизацию. хотя это не слишком большой штраф.

4
ответ дан 27 November 2019 в 00:42
поделиться

Поскольку y выходит за рамки синхронизированного метода, нет никаких гарантий, что его изменения будут видны в других потоках. Если вы хотите гарантировать, что изменения y будут одинаковыми для всех потоков, тогда все потоки должны использовать синхронизацию при чтении / записи y.

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

И да, JVM гарантирует, что пока блокировка удерживается, никакой другой поток не может войти в область кода, защищенную той же блокировкой.

-1
ответ дан 27 November 2019 в 00:42
поделиться

synchronize гарантирует, что только один поток может ввести блок кода. Но это не гарантирует, что изменения переменных, выполненные в синхронизированном разделе, будут видны другим потокам. Только потоки, входящие в синхронизированный блок, гарантированно увидят изменения. Эффекты синхронизации памяти в Java можно сравнить с проблемой блокировки с двойной проверкой применительно к C ++ и Java. Блокировка с двойной проверкой широко цитируется и используется как эффективный метод реализации отложенной инициализации в многопоточной среде. К сожалению, он не будет надежно работать независимо от платформы при реализации на Java без дополнительной синхронизации. При реализации на других языках, таких как C ++, это зависит от модели памяти процессора, переупорядочения, выполняемого компилятором, и взаимодействия между компилятором и библиотекой синхронизации. Поскольку ни один из них не определен в таком языке, как C ++, мало что можно сказать о ситуациях, в которых он будет работать. Чтобы заставить его работать в C ++, можно использовать явные барьеры памяти, но эти барьеры недоступны в Java.

1
ответ дан 27 November 2019 в 00:42
поделиться
Другие вопросы по тегам:

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