Гарантирует ли последнее поле видимость значений поля в другом потоке?

я использую 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 только в первом исполнении.

Другие вопросы по тегам