Будет ли JVM обновлять значение поля для всех потоков с использованием Happens-Before, без присвоения значения полю напрямую?

#java #multithreading #happens-before

#java #многопоточность #произойдет-before

Вопрос:

Я уже знаю, что если я напишу в энергонезависимое поле из другого потока, он, вероятно, мог бы кэшировать его, чтобы все остальные потоки не видели фактического значения. Но если я вызову, например start() , объект потока ПОСЛЕ присвоения значения filed, JVM обновит это значение для всех других потоков. Но будет ли JVM обновлять значение этого поля, если я выполню то же действие, но присвою значение полю не напрямую, как это: object.field = 100 но с помощью метода вызова object.setFiled(100) .

 public class Main {
int value;

public static void main(String[] args) {
    Main main = new Main();
    main.value = 100;
    new Thread(() -> System.out.println(main.getValue())).start();
    
 

В этом случае значение поля наверняка будет одинаковым для всех остальных потоков

 }

public int getValue() {
    return value;
}
}

public class Main {
private int value;

public static void main(String[] args) {
    Main main = new Main();
    main.setValue(100);
    new Thread(() -> System.out.println(main.getValue())).start();
 

Но будет ли результат таким же в этом случае?

 }

public int getValue() {
    return value;
}

public void setValue(int value) {
    this.value = value;
}
}
 

Комментарии:

1. Существует фундаментальное заблуждение, показанное во фразе «обновит это значение для всех других потоков». Это не то, что происходит. Гарантии предоставляются только для потока, запущенного через вызов to start() , в отношении записей, выполненных потоком, вызывающим start() до вызова. Никакая другая гарантия для любого другого потока не предоставляется.

Ответ №1:

Механизм того, как что-то обновляется в потоке, будь то прямой доступ к полю или через сеттеры, в данном случае не имеет значения. В JLS 17.4.5 указано, что «Вызов start() в потоке выполняется — перед любыми действиями в запущенном потоке». И в пределах одного потока «Если x и y являются действиями одного и того же потока, и x предшествует y в порядке программы, тогда [x происходит перед y]». В этом случае установщик метода или назначение поля, которое сделало бы значение видимым для исходного потока, также делает это значение видимым для вновь запущенного потока. Грубо говоря, все, что было видно потоку, вызывающему start() во время вызова start(), будет аналогичным образом видно новому потоку до тех пор, пока просматриваемый объект не будет изменен, и попытки просмотра состояния не изменят вещи (например. LinkedHashMap#get с accessOrder=true).