Ява: может синхронизированный блок чередовать? - PullRequest
0 голосов
/ 15 мая 2018

У меня неожиданный (по крайней мере для меня) вывод с этим кодом

public class Test {
    static boolean condition = false;

    void runme() {
        var reader = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("waiting for condition");
                    while (!condition) {}
                    System.out.println("condition is true");
                }
            }
        };

        var writer = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("condition is set to true");
                    condition = true;
                }
            }
        };

        new Thread(reader).start();
        new Thread(writer).start();

    }

    public static void main(String[] args) {
        new Test().runme();
    }
}

Исходя из документации, я ожидал тупика, если объект reader запускается первым, так как

  1. получает блокировку для this (вход в синхронизированный блок)
  2. печатает «ожидание условия»
  3. застревает в бесконечном цикле навсегда
  4. другой поток ожидает блокировки this, чтобы войти в свой собственный синхронизированный блок

Однако при некоторых прогонах кода я получаю вывод

waiting for condition
condition is set to true
condition is true

Я пропустилчто-то или я неправильно понял, как работают синхронизированные блоки / методы?

Ответы [ 3 ]

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

Два оператора synchronized (this) ссылаются на Runnable анонимные классы.
Таким образом, синхронизация двух экземпляров Runnable не работает на одной и той же блокировке.
Необходимо выполнить синхронизацию с экземпляром внешнего класса, чтобы заблокировать тот же монитор, например:

synchronized (Test.this) {...}

Кроме того, обратите внимание, что с помощью лямбды для реализации функционального интерфейса Runnable, такого как:

var writer = () -> {
    synchronized (this) {
        System.out.println("condition is set to true");
        condition = true;
    }
};

Вы можете сохранить фактический синтаксис (synchronized (this)) как this, в этом случае не ссылается на анонимный класс, который не существует, но ссылается на внешний экземпляр.

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

Ссылка this относится к текущему объекту.Внутри метода экземпляра внутреннего класса this ссылается на текущий объект внутреннего класса.Если вы хотите получить доступ к текущему объекту внешнего окружающего класса, вам нужно использовать следующее: OuterClassName.this.

В вашем случае у вас есть два отдельных объекта, имеющих анонимные классы.Синхронизированные блоки не разделяют общий объект для блокировки, поэтому фактически синхронизация не выполняется, а оба потока работают параллельно независимо.

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

В вашем коде synchronized(this) относится к двум разным объектам.Так что ни один код не блокирует другой, они просто запускаются одновременно.

Другой способ взглянуть на это - не использовать var или локальные классы.Просто объявите два класса верхнего уровня, которые делают то же самое, что и reader и writer:

//       var reader = new Runnable() {
class Reader implements Runnable {
        @Override
        public void run() {
            synchronized (this) {
                System.out.println("waiting for condition");
                while (!condition) {}
                System.out.println("condition is true");
            }
        }
    }


//        var writer = new Runnable() {
class Writer implements Runnable {
        @Override
        public void run() {
            synchronized (this) {
                System.out.println("condition is set to true");
                condition = true;
            }
        }
    }

Должно быть совершенно очевидно, что this в этом случае относится к экземпляру каждого класса, а неодин и тот же объект, поэтому эти два класса никогда не смогут блокировать друг друга.

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