Рассмотрим эту программу Java: < /p>
Код: Выделить всё
public class Loop {
static volatile boolean flag;
public static void main(String[] args) {
new Thread(() -> flag = true).start();
while(!flag);
}
}
< /code>
выражено словами: есть два потока. Один поток ждет летучего логического флага Насколько я понимаю, спецификация языка Java не гарантирует, что эта программа останавливается - см. Ниже для моих рассуждений.
Однако я не уверен, что мое понимание здесь правильно, поскольку программы, аналогичные этим, иногда представляются в качестве примеров правильной синхронизации Java. < /p>
рассуждения < /h3>
Все ссылки на JLS см. Java SE 23. Кроме того, если я ссылаюсь на чтения или записывает, не явно указав, на какую переменную они ссылаются, я имею > Для краткости, потому что эти чтения и записи являются наиболее актуальными. < /p>
§17.4 Определяет модель памяти Java (JMM) - она классифицирует, какое поведение программы является действительным, а какие нет. §17.4.1 - §17.4.7. Укажите понятие достоверных выполнений ; §17.4.8. Дополнительное ограничение казней на те, которые не нарушают причинно -следственную связь; и §17.4.9 Определяет наблюдаемое поведение s программ, опираясь на действительные выполнения. Первый шаг, я считаю, что для каждого n> 0 существует достоверное выполнение данной программы, которая содержит ровно n летучие чтения, и нет внешних действий (за исключением действия выполнения введено §17.4.9). В частности, это выполнение, чьи синхронизационные действия, упорядоченные по порядку синхронизации, являются: < /p>
[*] Два потока, A и B, начало (т.е. Действие; см. §17.4.2).
b делает одну нестабильную запись значения true
[*] a делает еще одно летучие чтения, видя летучую запись, выполняемую b. < Br /> [*] Оба A и B прекращают (то есть выполните свое синтетическое последнее действие; см. §17.4.2). < /li>
< /ol>
Я считаю, что эти казни выполнить все требования для действительных казней, приведенных в §17.4.1 - §17.4.7, и, кроме того, условия для повторных причинности, приведенных в §17.4.8. Утверждая, что с математической точностью очень утомительно, поэтому я буду опустить его для краткости (этот вопрос уже слишком длинный!)
наблюдаемое поведение
< Br /> Далее, я утверждаю, что поведение, состоящее только из действий Hang < /code>, является наблюдаемым поведением данной программы. < /p>§17.4.9 : < /p>
тки... O Должно быть подмножеством действий E, A, и должно содержать только конечное количество действий, даже если A содержит бесконечное количество действий. Кроме того, если действие y находится в o, и либо hb (x, y) или около того (x, y), то x находится в o. p>
Поведение B является допустимым поведением программы P, если и только тогда, когда B является конечным набором внешних действий и либо: < /p>
- [...] < /li>
Существует множество действий, таких чтобы B состоит из заветного действия плюс все внешние действия в O и для всех k ≥ | o |, Существует выполнение e of p с действиями a, и существует набор действий, такие как:
Оба o и o 'являются подмножествами, которые выполняют требования для наборов наблюдаемых действий. < /li>
o ⊆ o '⊆ a < /li>
| o' | ≥ k - o ' - o не содержит внешних действий
Выберите b , чтобы быть набором Singleton, содержащего только действие Hang и O , чтобы быть пустым набором. Теперь мы должны показать условия, приведенные здесь для всех k ≥ 0 .
Для любого такого данного k выберите e , чтобы быть Выполнение с ровно k считывает. Затем - это набор, по крайней мере, k actions, который содержит ровно одно внешнее действие - в частности, выполнение . Теперь выберите o ', чтобы быть без этого действия . Затем все четыре условия из §17.4.9 выполняются:
- " /code>, который выполняет требования для наборов наблюдаемых действий ":
Оба o и o ' являются подмножествами . < /li>
Оба конечные: | o | = 0 и | o '| = n+x где x - постоянное количество действий за пределами n reads. - не содержит действий, что означает, что условие, относящееся к Hb , является вероисповеданием.
Код: Выделить всё
O - содержит все действия из , за исключением выполнения , и нет x такого, что hb (выполнение времени, x) , который был бы единственным способом принуждения executionTermination быть в o '.
Код: Выделить всё
O'
Код: Выделить всё
O ⊆ O' ⊆ A[*]"
Код: Выделить всё
|O'| ≥ k[*]"
Код: Выделить всё
O' - O< H3> Поведение реальных JVMS < /h3>
I немного экспериментировал с использованием OpenJDK - в частности, Oracle Build 23+37-2369 - на машине x86_64. < /p>
Я не смог сделать настоящий OpenJDK/Oracle JDK подвеса для вариантов этой программы, пока флаг является изменчивым - даже не введя «поощрение», как вызовы в Thread :: Sleep или Thread :: setPriority . > Для получения сгенерированной сборки) как C1, так и C2 сохраняют состояние цикла не повреждены.
Код: Выделить всё
0x00007f7f1855e950: movzx r8d,BYTE PTR [r10+0x70]
0x00007f7f1855e955: mov r11,QWORD PTR [r15+0x450] ; ImmutableOopMap {r10=Oop }
;*ifeq {reexecute=1 rethrow=0 return_oop=0}
; - (reexecute) Loop::main@18 (line 6)
0x00007f7f1855e95c: test DWORD PTR [r11],eax ; {poll}
0x00007f7f1855e95f: test r8d,r8d
0x00007f7f1855e962: je 0x00007f7f1855e950
< /code>
Единственный вариант этой программы, где OpenJdk /Oracle JDK видит, если Flag < /code> стал нелетующим. Тем не менее, это меняет вещи даже с точки зрения JMM, потому что тогда больше нет никаких HB-Edges от действия по записи до любого из чтения. На практике на практике C2 «оптимизирует» цикл в бесконечный цикл, например,: < /p>
0x00007ffa5455e960: mov r10,QWORD PTR [r15+0x450] ; ImmutableOopMap {}
;*ifeq {reexecute=1 rethrow=0 return_oop=0}
; - (reexecute) Loop::main@18 (line 6)
0x00007ffa5455e967: test DWORD PTR [r10],eax ; {poll}
0x00007ffa5455e96a: jmp 0x00007ffa5455e960
< /code>
Вопросы < /h2>
[list]
[*] Является ли мое понимание JLS /JMM в том, что программа выше не останавливается? Если нет, где моя ошибка? , или его вариант (до тех пор, пока флаг [/list]
Подробнее здесь: https://stackoverflow.com/questions/794 ... starvation
Мобильная версия