|
|
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
Коллеги, может кто-то просветить а то голова уже совсем не соображает под конец рабочего дня, есть два потока, которые выполняют некую последовательность действий с одним объектом, изобразил схематически: Код: java 1. 2. 3. 4. 5. TreadA TreadBr = 1 r = 2lockT lockTx=3 x=4unlockT unlockT Стартуют два потока, ThreadA стартует немного раньше и захватывает lockT первым (вроде как в точке захвата происходит happens before?) ThreadB пытается захватить lockT и блочится пока ThreadA отдаст лок. ThreadA отдает лок, ThreadB - захватывает, чему равно значение переменной r (она не volatile)? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.11.2014, 21:07 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
Вроде как TreadA в момент unlockT должен синхронизироваться с основной памятью, а ThreadB в момент захвата должен вычитать из этой памяти, изходя из этой логики значение r будет 1? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.11.2014, 21:11 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1, вот здесь написано https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html , что Commonly, a lock provides exclusive access to a shared resource: only one thread at a time can acquire the lock and all access to the shared resource requires that the lock be acquired first. то есть, 1, если я, правильно вас понял. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.11.2014, 21:20 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
Остаётся только чесать репу и думать какой смысл вкладывается в lockT ? Может monitorenter? Ох уж эти пейсатели. Убить мало... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.11.2014, 22:22 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1, Может быть 1, может быть 2. Как повезет. У вас же нет hb-отношений, которые запрещают видеть значение 2. Точно не может быть default value (0) при условии, что нет других потоков. P.S. HB и Main Memory не совместимы. Main Memory была только в JLS 2 (может и в JLS 1). В JLS 3 ее заменили на HB. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.11.2014, 23:15 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
maxkar +1 автор ThreadA стартует немного раньше и захватывает lockT первым +Из того, что ThreadA стартует раньше еще не следует, что он захватит лок первым. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.11.2014, 23:34 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
mr_virtus, Спасибо за ссылку! Там вообщем про локи, а мне бы про этот конкретный случай, который вроде как не попадает под то описание. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 01:18 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
maytonОстаётся только чесать репу и думать какой смысл вкладывается в lockT ? Может monitorenter? Ох уж эти пейсатели. Убить мало... Пример далекий от продакшена, не принимайте близко к сердцу :) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 01:18 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
maxkar, Огромное спасибо за разъяснение, под HB вы имеете ввиду happens before? А если бы я убрал r=2, то тут был бы happens before (TheadB увидел бы что r=1)? Вообщем надо читать JLS спецификаию, а она у меня тяжело идет( ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 01:25 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
no56892maxkar +1 автор ThreadA стартует немного раньше и захватывает lockT первым +Из того, что ThreadA стартует раньше еще не следует, что он захватит лок первым. Хм, интересная мысль. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 01:26 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1Вроде как TreadA в момент unlockT должен синхронизироваться с основной памятью, а ThreadB в момент захвата должен вычитать из этой памяти, изходя из этой логики значение r будет 1? j.u.c.l.Lock :Memory Synchronization All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in section 17.4 of The Java™ Language Specification: A successful lock operation has the same memory synchronization effects as a successful Lock action. A successful unlock operation has the same memory synchronization effects as a successful Unlock action. Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.В JMM-17.4.2 указывается, что Lock/Unlock - синхронизирующие операции. Таким образом: 1. Когда один поток захватывает блокировку, то попытка блокировки из второго потока будет неудачной и второй поток имеет право видеть любое из возможных значений. 2. Когда один поток освобождает блокировку, то и это и (теперь успешная) попытка блокировки из второго потока будут "синхронизированы по памяти", а значит второй поток будет видеть то, что установил первый. "По моему так" (ц) Винни-Пух голосом Евгения Леонова ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 06:40 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
Basil A. SidorovYamahaR1Вроде как TreadA в момент unlockT должен синхронизироваться с основной памятью, а ThreadB в момент захвата должен вычитать из этой памяти, изходя из этой логики значение r будет 1? j.u.c.l.Lock :Memory Synchronization All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in section 17.4 of The Java™ Language Specification: A successful lock operation has the same memory synchronization effects as a successful Lock action. A successful unlock operation has the same memory synchronization effects as a successful Unlock action. Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.В JMM-17.4.2 указывается, что Lock/Unlock - синхронизирующие операции. Таким образом: 1. Когда один поток захватывает блокировку, то попытка блокировки из второго потока будет неудачной и второй поток имеет право видеть любое из возможных значений. 2. Когда один поток освобождает блокировку, то и это и (теперь успешная) попытка блокировки из второго потока будут "синхронизированы по памяти", а значит второй поток будет видеть то, что установил первый. "По моему так" (ц) Винни-Пух голосом Евгения Леонова Ага, спасибо за разъяснения, т.е. в момент освобождения блокировки первым потоком произойдет "синхронизация по памяти" и второй поток увидит что r была установлена в 1, но какое из двух значений он выберет никто сказать точно не может. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 12:49 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
"Вот это сходил за хлебушком разъяснил" ... Как-то вы очень извратили мою мысль. Первый поток успешно захватывает блокировку на объекте j.u.c.l.Lock. В этом потоке гарантируется: 1. Отсутствие переупорядочиваний, которые иначе могли бы сделать java- или JIT-компиляторы; 2. Вставка специальных команд, отменяющих возможные аппаратные переупорядочивания или/и отложенные записи. Второй поток пытается захватить ту же самую блокировку, но не неудачно. В этом случае не гарантируется ничего. В частности, если код второго потока кешировал значение какой-либо переменной в регистрах, то он не обязан перечитывать это значение из памяти. Первый поток успешно освобождает блокировку и снова синхронизирует состояние памяти. После этого второй поток успешно захватывает блокировку и тоже синхронизируется. Теперь он гарантированно видит всё, что изменил первый. P.S. 1. Как и ранее - IMHO; 2. В процессе синхронизации можно получить "потерянные обновления", так что код должен быть "правильным" ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 16:43 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
Basil A. Sidorov, Спасибо за ответ. Я хотел бы у вас уточнить, что значит "синхронизирует состояние памяти", записывать все свои изменения в память что бы она была видна другим потокам? Я правильно вас понимаю что вы говорите о синхронизации Shared Memory? Basil A. SidorovВ этом случае не гарантируется ничего. В частности, если код второго потока кешировал значение какой-либо переменной в регистрах, то он не обязан перечитывать это значение из памяти. Первый поток успешно освобождает блокировку и снова синхронизирует состояние памяти. После этого второй поток успешно захватывает блокировку и тоже синхронизируется. Теперь он гарантированно видит всё, что изменил первый. Вот тут, не могли бы вы разъяснить немного подробнее на моем примере. Давайте представим что второй поток не вычитывал значение r из памяти в момент неудачного захвата лока (т.е. r = 2). Первый поток освобождает блокировку, второй поток захватывает, синхронизирует и видит что r = 1. Второй поток освобождает и синхронизирует, теперь изменения второго потока r=2 становятся видны другим потокам. Поправьте плиз если где-то ошибся. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 17:45 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1Второй поток освобождает и синхронизирует, теперь изменения второго потока r=2 становятся видны другим потокам. Поправьте плиз если где-то ошибся. неправильно, может да а может и нет. Присвоение r=2 до лока, значит никаких гарантий нет. Если бы это было после лока и перед анлоком - тогда безусловно ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 17:57 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
забыл никYamahaR1Второй поток освобождает и синхронизирует, теперь изменения второго потока r=2 становятся видны другим потокам. Поправьте плиз если где-то ошибся. неправильно, может да а может и нет. Присвоение r=2 до лока, значит никаких гарантий нет. Если бы это было после лока и перед анлоком - тогда безусловно Тогда присвоение r=1 первым потоком также не видно второму потоку после освобождения лока? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 18:06 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1забыл никпропущено... неправильно, может да а может и нет. Присвоение r=2 до лока, значит никаких гарантий нет. Если бы это было после лока и перед анлоком - тогда безусловно Тогда присвоение r=1 первым потоком также не видно второму потоку после освобождения лока? Всмысле нет никаких гарантий что r=1 будет видно второму потоку ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 18:06 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1YamahaR1пропущено... Тогда присвоение r=1 первым потоком также не видно второму потоку после освобождения лока? Всмысле нет никаких гарантий что r=1 будет видно второму потоку Почему нет? Перейди от абстрактного r =1 к сохранению в List, допустим у нас вначале пустой лист Поток А ложит туда 1, поток В ложит 2. Так как переменные не volatile, то на момент lockT вторым потоком может быть три состояния листа - только 2 - если поток В взял лок до того как взял его поток А(или А еще не выполнил put(1) 1, 2 - если поток выполнил пут(1) взял лок, отпустил и поток B сделал put(2) перед локом 2,1 - если поток В сделал put(2), зачем-то решил сбросить кэш(так как не волатайл - то он может делать это когда угодно) и заснул перед локом(допустим свичнулся). А потом поток А сделал put(1), взял и отпустил лок. Теперь если поток В будет читать лист - то там будут именно эти значения. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 19:15 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1Второй поток освобождает и синхронизирует, теперь изменения второго потока r=2 становятся видны другим потокам. Поправьте плиз если где-то ошибся.Не "становятся видны", а "могут быть видны". Потому как со всеми остальными потоками будет та же самая ерундень: пока поток не засинхронизировался - он может видеть любое допустимое значение. Грубо говоря, если реализована некая схема блокировки, то её обязаны использовать все участники. Или схема может развалиться в любой момент. Простого "с нашей стороны все пули вылетели" - ещё недостаточно. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.11.2014, 19:42 |
|
||
|
Happens Before
|
|||
|---|---|---|---|
|
#18+
YamahaR1, Да, hb==happens-before. Про него нужно понять очень важный момент. hb не определяет, какое значение может увидеть чтение. Оно определяет, какие значения _не может_ увидеть чтение. И определяется это как раз через тразнитивность записей. Чтобы было понятнее, нужно уточнить ваш пример. ThreadA ThreadBr=5r=6r=1r=2lock lockunlock unlock-- a = r-- b = r-- c = r-- print(r) Если в этом случае у вас ThreadA выполнился перед ThreadB, будет отношение hb(r=1, print(r)) (через тразнитивность отношения). Кроме того, hb(r=5, r=1) (program order в threadA). Поэтому print(r) не может видеть (при данном порядке выполнения) запись r=5. Но может видеть r=1. Аналогично внутри потока B (там просто program order) не может быть прочитано 6, но может быть прочитано 2. А вот присваивания r=1 и r=2 - равнозначны с точки зрения модели памяти. И между ними нет никакого отношения happens-before. Поэтому может быть напечатано как 1, так и 2. Но самое интересное даже не это. А то, что a, b и c могут (чисто теоретически) иметь совершенно неочевидные значения. Например, может быть (a=1,b=2,c=1). Или, например (a=2, b=1, c=2). И это все при том, что ThreadA выполнился до ThreadB (т.е. мы можем внутри lock/unlock условие проверить, например). ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 13.11.2014, 01:23 |
|
||
|
|

start [/forum/topic.php?fid=59&msg=38802898&tid=2126276]: |
0ms |
get settings: |
11ms |
get forum list: |
14ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
57ms |
get topic data: |
9ms |
get forum data: |
2ms |
get page messages: |
66ms |
get tp. blocked users: |
2ms |
| others: | 245ms |
| total: | 412ms |

| 0 / 0 |
