Синхронизация Java по переменной экземпляра - PullRequest
0 голосов
/ 15 мая 2018

При получении блокировки для переменной экземпляра JVM "обновляет" состояние целого экземпляра или только состояние этой конкретной переменной?

Рассмотрим реализацию следующего класса

public class SyncObject {
    private boolean flag = false;
    private final Object lock = new Object();
    private volatile Object visibility = null;

    public void setFlag() {
        a = true;
    }

    public boolean getFlag() {
        return flag;
    }

    public void refreshLock() {
        synchronized(lock){
            ;
        }
    }

    public void refreshVisibility() {
        Object dummy = visibility;
    }
}

Два потока T1 и T2, операторы выполняются сверху вниз

T1 -> setFlag()
T2 -> getFlag()

На данный момент очевидно, что T2, скорее всего, увидит устаревшие данные, поскольку не использовались ни синхронизация, ни ключевое слово volatile.

Однако, если T2 впоследствии отправляет сообщения

T2 -> refreshLock()
T2 -> getFlag()

метод getFlag() возвращает наиболее актуальное значение? Согласно тесту, который я сделал , он делает .

Если это действительно так, может кто-нибудь объяснить, пожалуйста , почему получение блокировки переменной экземпляра обновляет состояние всего владельца, а не только состояние этой конкретной переменной ? Я уже взглянул на JLS 17.4.4. , но до сих пор не могу понять.

Test

Я использовал следующий код для проверки поведения, описанного выше

public class Main {

    public static void main(String[] args) {
        final SyncObject sObject = new SyncObject();

        Thread background = new Thread(() -> {
            while(!sObject.getFlag()){
                // if line below is uncommented, thread finishes in roughly 1s, otherwise it loops forever
                //sObject.refreshLock();
            }
        });

        background.start();
        TimeUnit.SECONDS.sleep(1);
        sObject.setFlag();
    }
}

Обновление # 1

Я немного отредактировал реализацию SyncObject и добавил атрибут visibility, а также метод refreshVisibility. Если метод refreshLock в тестовом примере заменяется на refreshVisibility, поведение остается таким же, и выполнение потока завершается примерно через 1 с.

1 Ответ

0 голосов
/ 15 мая 2018

Вы должны взглянуть на следующую точку, то есть отношение «до и до», https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5.

Вы можете быть уверены, что операции записи, выполняемые потоком A, видны потоку B, если случится, прежде чем отношения сохраняются.И это тот случай, когда у вас есть потоки, блокирующие монитор по очереди, или первая запись, а затем другая запись из переменной volatile.(плюс есть другие случаи, перечисленные в документе).

Кроме того, как объяснено в руководстве, видимость не ограничивается одной переменной, но каждой переменной, которая изменилась до «барьера».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...