Гарантирует ли последнее поле видимость значений поля в другом потоке?
я использую
JCStress
для проверки последней переменной. Я знаю это,
final
может использоваться, чтобы убедиться, что при создании объекта другой поток, обращающийся к этому объекту, не видит этот объект в частично сконструированном состоянии. Теперь у меня есть класс A.java как таковой
public class A {
final int f;
A() {
this.f = 42;
}
}
Насколько мне известно, конструктор должен выполняться как таковой
A temp = <new>
temp.f = 42
<freeze value>
fv = temp
Сейчас я использую указанный ниже тест.
@JCStressTest
@State
public class FinalField {
A a;
@Actor
public void writer() {
a = new A();
}
@Actor
public void reader(I_Result result) {
A ta = a;
if (ta != null) {
result.r1 = ta.f;
}
}
}
Теперь почему я вижу значение 0 в моем выводе? Моя архитектура процессора - x86, поэтому переупорядочивать магазины с загрузками тоже не имеет смысла. На выходе я получаю
0 94,922,153 FORBIDDEN No default case provided, assume FORBIDDEN
42 48,638,587 ACCEPTABLE Final value initialized
Еще одна необычная вещь, которую я нашел, - это то, что когда я объявляю поле
a
в виде
static
. На выходе я получаю только 42, и почему?
42 299,477,390 ACCEPTABLE Final value initialized
3 ответа
Объяснение действительно простое. Какое значение по
умолчаниюresult.r1
? Какой это тип? Это значение по умолчанию для
int
является . Итак, когда это
if (ta != null)
не бывает, то есть
ta
является
null
, ваш код ничего не сделает. Это «ничего» означает сохранение значения по умолчанию, то есть (вы уже знаете)
zero
. Так когда
ta == null
(и неявно
a == null
), ты уходишь
r1
быть, хотя вы явно этого не делаете .
Решение тривиальное:
@Actor
public void reader(I_Result result) {
A ta = a;
if (ta != null) {
result.r1 = ta.f;
} else {
result.r1 = -1;
}
}
а также:
@JCStressTest
@State
@Outcome(id = "42", expect = Expect.ACCEPTABLE, desc = "42 is OK")
@Outcome(id = "-1", expect = Expect.ACCEPTABLE, desc = "-1 is OK too")
И теперь ваш код никогда не будет отображаться
0
, если вы читаете
a
чтобы быть ненулевым, вы всегда будете читать
a.f
быть . Насколько вы понимаете, да, все потоки увидят
42
как только они увидят ссылку на экземпляр
A
- это
JLS
гарантия.
Другой ответ уже объяснял, почему у вас проблемы с
0
а также
static
.
Но даже после устранения этих проблем может быть сложно воспроизвести частичную инициализацию.
Поэтому я бы порекомендовал вам взглянуть на исходный код JCStress: он содержит образцы, и один из них (JMMSample_06_Finals) уже делает то, что вы хотите.
Теперь почему я вижу значение 0 в моем выводе?
Это тот случай, когда
a == null
(и поэтому
result.r1
останки
0
) в твоей
reader()
метод.
когда я объявляю поле статическим. На выходе я получаю только 42, и почему?
Вы написали аннотацию
@State
, поэтому JCStress создает новый экземпляр для каждого выполнения.
Если это поле экземпляра в, то оно изначально присутствует при каждом выполнении.
Если
a
статическое поле в
FinalField
, то он распределяется между всеми исполнениями и
null
только в первом исполнении.