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

В главе 17 JLS, он вводит понятие: происходит - прежде, чем согласовано.

Набор действий A является согласованным, если для всех чтений r в A, где W (r) - это действие записи, наблюдаемое r, то не так, что либо hb (r, W (r)), либо что там существует запись w в A такая, что wv = rv и hb (W (r), w) и hb (w, r) "

В моем понимании это равносильно следующим словам: ..., это тот случай, когда ни ... ни ...

Итак, мои первые два вопроса:

  • я правильно понимаю?
  • что означает "w.v = r.v"?

Также приводится пример: 17.4.5-1

Thread 1 Thread 2

B = 1; A = 2; 

r2 = A; r1 = B; 

В первом порядке исполнения:

1: B = 1;

3: A = 2;

2: r2 = A;  // sees initial write of 0

4: r1 = B;  // sees initial write of 0

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

Насколько я понимаю, причина того, что и r2, и r1 могут видеть начальную запись 0, - это то, что A и B не являются изменчивым полем. Итак, мой четвертый вопрос: правильно ли я понимаю?

Во втором порядке исполнения:

1: r2 = A;  // sees write of A = 2

3: r1 = B;  // sees write of B = 1

2: B = 1;

4: A = 2;

Согласно определению согласованности «происходит до того, как», нетрудно понять, что этот порядок выполнения происходит до согласованности (если мое первое понимание верно). Итак, мои пятый и шестой вопросы: существует ли такая ситуация (чтение, запись, запись, которая происходит позже) в реальном мире? Если да, не могли бы вы привести мне реальный пример?


person newman    schedule 15.08.2012    source источник
comment
Мне нужно пояснение к примеру ... Если для обоих потоков присвоение выполняется до чтения, по крайней мере одно значение изменено до того, как они достигнут чтения. Как оба потока могут видеть 0?   -  person treaz    schedule 27.04.2013
comment
серьезно, что означает левый номер?   -  person Tim    schedule 24.05.2019


Ответы (5)


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

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

В обоих случаях использование volatile гарантирует, что чтение и запись происходят в согласованном порядке и оба потока видят одно и то же значение. Иногда это называют чтением из основной памяти, хотя это не обязательно, потому что кеши могут напрямую взаимодействовать друг с другом. (Таким образом, снижение производительности намного меньше, чем вы могли ожидать).

На обычных процессорах кэши являются «связными» (не могут содержать устаревшие / конфликтующие значения) и прозрачными, не управляются вручную. Сделать данные видимыми между потоками просто означает выполнить фактическую загрузку или сохранить инструкцию в asm для доступа к памяти (через кеши данных) и, возможно, дождаться, пока буфер хранилища истощится, чтобы отдать приказ wrt. другие последующие операции.

person Peter Lawrey    schedule 15.08.2012
comment
Большое спасибо за ваш ответ. Могу ли я считать, что все мои два понимания верны? И еще два вопроса: что означает w.v = r.v? что означает левый номер? Еще раз спасибо. - person newman; 16.08.2012
comment
В соответствии с моим пониманием, я считаю, что если программа правильно синхронизирована, то в ней по-прежнему возможна гонка данных. Итак, что вы думаете об этом мнении? - person newman; 16.08.2012
comment
У меня возник новый вопрос: позволяет ли правильно синхронизированная программа по-прежнему гоняться за данными? (Часть I), чтобы продолжить это обсуждение. Приглашаем вас присоединиться к новому вопросу. - person newman; 17.08.2012
comment
synchronized гарантирует, что вся память на входе и выходе из блока происходит в предсказуемом порядке, даже если вы не получили доступа в блоке. синхронизированные компоненты допускают различные условия гонки, например Вектор или Collections.syncrhonizedList, доступ к которому осуществляется несколько раз, может иметь состояние гонки между чтением и записью в эту коллекцию. - person Peter Lawrey; 17.08.2012

Модель памяти Java определяет частичный порядок всех ваших действий в вашей программе, который называется происходит-до.
Чтобы гарантировать, что поток Y может видеть побочные эффекты действия X (не имеет значения, если X произошло в другом потоке или нет) связь происходит до определяется между X и Y.
Если такая связь отсутствует, JVM может повторно -упорядочить операции программы.
Теперь, если переменная совместно используется и используется многими потоками и записывается (по крайней мере) одним потоком, если чтение и запись не упорядочены с помощью , происходит до отношения, тогда у вас есть гонка данных.
В правильной программе нет гонок данных.
Пример: 2 потока A и B синхронизируются при блокировке X.
Thread A получает блокировку (теперь Thread B заблокировано ) и выполняет операции записи, а затем снимает блокировку X. Теперь Thread B получает блокировку X, и поскольку все действия Thread A были выполнены перед снятием блокировки X, они упорядочены до действий Thread B, получившего блокировку X после потока A (также видимого для Thread B).
Обратите внимание, что это происходит для действий, синхронизированных с той же блокировкой. Никаких не происходит до того, как взаимосвязь между потоками синхронизируется на разных блокировках.

person Cratylus    schedule 15.08.2012
comment
Большое спасибо за ваш ответ. Могу ли я считать, что все мои два понимания верны? И еще два вопроса: что означает w.v = r.v? что означает левый номер? Еще раз спасибо. - person newman; 16.08.2012
comment
@newman: r.v означает: read of a variable v и и w.v означает: write to variable v. Это объясняется в том же абзаце: We say that a read r of a variable v is allowed to observe a write w to v if, in the happens-before partial order of the execution trace - person Cratylus; 16.08.2012
comment
@newman Возможно, вас заинтересует это обсуждение, которое показывает, как вы можете использовать этот абзац для подтверждения правильности параллельного кода (т. е. отсутствия гонки за данные). - person assylias; 16.08.2012
comment
Большое спасибо за ваш ответ. Я понял, что означает w.v и r.v, но что означает w.v = r.v, другими словами, что означает =? Я думаю, это не означает равно, потому что равно ==. Не могли бы вы объяснить еще один шаг? - person newman; 16.08.2012
comment
@newman Это означает, что чтение и запись относятся к одной и той же переменной - он ничего не говорит о значении переменной, записанной / увиденной w / r. - person assylias; 16.08.2012
comment
@newman: Выберите ответ, который вам больше всего помог, и отметьте его как принятый (если вы думаете, что поняли свою проблему) - person Cratylus; 16.08.2012
comment
Спасибо за напоминания. Я сделал это. Приглашаем вас присоединиться к дальнейшему обсуждению. - person newman; 17.08.2012
comment
У меня возник новый вопрос: позволяет ли правильно синхронизированная программа по-прежнему гоняться за данными? (Часть I), чтобы продолжить это обсуждение. Приглашаем вас присоединиться к новому вопросу. - person newman; 17.08.2012

Давайте посмотрим на определения в теории параллелизма:

Атомарность - свойство операции, которая может быть выполнена полностью как одна транзакция и не может быть выполнена частично. Например, Atomic operations [Пример].

Видимость - если одна ветка внесла изменения, они будут видны другим цепочкам. volatile до Java 5 с happens-before

Порядок - компилятор может изменять порядок операций / инструкций исходного кода для некоторых оптимизаций.

Например, happens-before, что-то вроде memory barrier, которое помогает решить проблемы Visibility и Ordering. Хорошими примерами того, что случилось раньше, являются volatile [About], synchronized monitor [О программе]

Хорошим примером atomicity является реализация _11 _ (_ 12_) шаблона _13 _ (_ 14_), который должен быть атомарным и позволяет изменять переменную в многопоточной среде. Вы можете написать свою собственную реализацию, если CTA:

  • volatile + synchronized
  • java.util.concurrent.atomic с sun.misc.Unsafe (выделение памяти, создание экземпляров без вызова конструктора ...) из Java 5, который использует JNI преимущества ЦП.

CAS алгоритм имеет три параметра (A (адрес), O (старое значение), N (новое значение)).

If value by A(address) == O(old value) then put N(new value) into A(address), 
else O(old value) = value from A(address) and repeat this actions again

Бывает-раньше

Официальный документ

Два действия могут быть упорядочены отношением "произошло до". Если одно действие происходит раньше другого, то первое становится видимым и упорядоченным перед вторым.

введите описание изображения здесь

volatile [About] в качестве примера

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

Давайте посмотрим на пример:

// Definitions
int a = 1;
int b = 2;
volatile boolean myVolatile = false;

// Thread A. Program order
{
    a = 5;
    b = 6;
    myVolatile = true; // <-- write
}

//Thread B. Program order
{
    //Thread.sleep(1000); //just to show that writing into `myVolatile`(Thread A) was executed before

    System.out.println(myVolatile); // <-- read
    System.out.println(a);  //prints 5, not 1
    System.out.println(b);  //prints 6, not 2
}

Видимость - когда Thread A изменяет / записывает непостоянную переменную, он также помещает все предыдущие изменения в ОЗУ - Основную память как в результате все неизменяемые переменные будут актуальными и будут видны другим потокам.

Заказ:

  • Все операции перед записью в изменчивую переменную в Thread A будут вызываться раньше. JVM может изменить их порядок, но гарантирует, что ни одна операция до записи в изменчивую переменную в Thread A не будет вызвана после нее.

  • Все операции после чтения изменчивой переменной в Thread B будут вызываться после. JVM может изменить их порядок, но гарантирует, что перед ней не будет вызвана ни одна операция после чтения изменчивой переменной в Thread B.

[Параллелизм против параллелизма]

person yoAlex5    schedule 26.12.2019
comment
Что касается порядка, это не только компилятор, который изменяет порядок или воспринимаемый порядок (инструкции могут выполняться по порядку, но их влияние на память не может быть замечено в том же порядке другими ядрами / процессорами). Архитектуры конвейерной обработки ЦП могут изменять и изменяют также порядок инструкций, которые не зависят от вывода друг друга. - person Erwin Bolwidt; 12.03.2020

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

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

Очевидно, что с точки зрения настенных часов чтение не может увидеть эффекта записи, которая еще не произошла.

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

person assylias    schedule 15.08.2012
comment
Большое спасибо за ваш ответ. Могу ли я считать, что все мои два понимания верны? И еще два вопроса: что означает w.v = r.v? что означает левый номер? Еще раз спасибо. - person newman; 16.08.2012
comment
В соответствии с моим пониманием, я считаю, что если программа правильно синхронизирована, то в ней по-прежнему возможна гонка данных. Итак, что вы думаете об этом мнении? - person newman; 16.08.2012
comment
@newman Я не уверен = ›Я предпочитаю быть простым и избегать гонок за данные. - person assylias; 16.08.2012
comment
Большое спасибо. Завтра продолжу эту тему. Увидимся на следующий день. - person newman; 16.08.2012
comment
Из JLS можно сделать два вывода: C1: если программа не имеет гонок данных, то все ее выполнения будут выглядеть последовательно согласованными. C2: Если программа правильно синхронизирована, то все исполнения программы будут выглядеть последовательно согласованными. Если верно другое направление C1, то мы получаем следующий вывод: C3: Если программа правильно синхронизирована, то эта проблема не имеет гонки данных. Но, к сожалению, в JLS такого направления нет, поэтому я делаю такой вывод: - person newman; 17.08.2012
comment
если программа правильно синхронизирована, то в программе все еще допускается наличие гонки за данные. Но я думаю, что этого недостаточно, я хочу получить такой же вывод другими твердыми способами или доказать ложность этого вывода, даже неформальным образом. Прежде всего, я думаю, что образец кода, который показывает последовательное согласованное выполнение многопоточной программы, в которой он содержит гонку данных, полезен для понимания и решения этого вопроса. После серьезных размышлений я все еще не могу найти подходящий образец. Не могли бы вы дать мне образец кода? - person newman; 17.08.2012
comment
Честно говоря, это становится очень теоретическим, и я не лучший человек, чтобы отвечать на эти вопросы. - person assylias; 17.08.2012
comment
У меня возник новый вопрос: позволяет ли правильно синхронизированная программа по-прежнему гоняться за данными? (Часть I), чтобы продолжить это обсуждение. Приглашаем вас присоединиться к новому вопросу. - person newman; 17.08.2012
comment
Я чувствую себя немного странно, воскрешая это, но вы говорите ...that a read that comes after a write in your program order..., а happens before consistency не имеет ничего общего с порядком программ. Это правило гласит, что чтение учитывает либо запись в happens-before, либо любое другое чтение (racy). Это правило определяет связь между порядком synchronizes-with, который создает happens-before order между несколькими потоками. - person Eugene; 26.11.2020

Q1: правильно ли я понимаю?

A: Да

Q2: что означает w.v = r.v?

A: Значение w.v такое же, как и r.v.

Q3: Что означает левый номер?

A: Я думаю, что это идентификатор оператора, как показано в Таблице 17.4-A. Удивительные результаты, вызванные переупорядочиванием операторов - исходный код. Но вы можете игнорировать это, потому что это не относится к случаю другого порядка выполнения, который происходит до того, как согласованный: Итак, левое число - дерьмо полностью. Не придерживайтесь этого.

Q4: Насколько я понимаю, причина того, что как r2, так и r1 могут видеть начальную запись 0, - это то, что поля A и B не являются изменчивыми. Итак, мой четвертый вопрос: правильно ли я понимаю?

A: Это одна из причин. повторный заказ тоже можно сделать. Программа должна быть правильно синхронизирована, чтобы избежать противоречивого поведения, которое может наблюдаться при изменении порядка кода.

Q5 и 6: Во втором порядке выполнения ... Итак, мои пятый и шестой вопросы: существует ли такая ситуация (чтение, записи, которые происходят позже) в реальном мире? Если да, не могли бы вы привести мне реальный пример?

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

время 1: поток 2: A = 2

time 2: Thread 1: B = 1 // Без синхронизации здесь можно чередовать B = 1 потока 1

time 3: Thread 2: r1 = B // значение r1 равно 1

time 4: Thread 1: r2 = A // значение r2 равно 2

Примечание. Выполнение происходит - прежде чем согласовано, если его набор действий происходит - до согласованного

person Bruce Zu    schedule 11.04.2021