Могут ли два потока выполнить один и тот же синхронизированный блок кода на одном и том же объекте, если они используют разные мониторы?

#java #multithreading #concurrency #parallel-processing #synchronized

#java #многопоточность #параллелизм #параллельная обработка #синхронизированный

Вопрос:

Я новичок в параллелизме Java и пытаюсь лучше понять мониторы.

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

 class Entity() {
    public void myMethod(Object monitor) {
        synchronized(monitor) {
            // critical stuff
        }
    }
}
 

Могут ли два потока одновременно входить в этот раздел на одном и том же объекте, если они используют разные объекты для монитора?

 final Entity myEntity = new Entity();
for (int i = 0; i < 3; i  ) {
    new Thread() {
        public void run() {
            // Can these all run concurrently?
            myEntity.myMethod(new Object());
        }
    }.start();
}
 

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

Было трудно найти документацию по этому вопросу, потому что в учебниках, похоже, в основном просто используется «this» в качестве монитора.

Ответ №1:

Могут ли два потока одновременно входить в этот раздел на одном и том же объекте, если они используют разные объекты для монитора?

Из руководства по oracle можно прочитать:

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

Неофициально это означает, что можно синхронизировать, используя любую Java Object . Блок, заключенный в предложение synchronized для одного экземпляра объекта, будет выполняться последовательно, т. Е. Выполняться потоком, удерживающим блокировку синхронизируемого объекта.

Могут ли два потока одновременно входить в этот раздел на одном и том же объекте, если они используют разные объекты для монитора?

Да, несколько потоков могут выполнять (параллельно) одну и ту же область кода, обернутую synchronized предложением, пока каждый из этих потоков синхронизируется с использованием разных экземпляров объекта.

Также можно синхронизировать, используя class сам, а не его экземпляры:

  synchronized (SomeClass.class){
     System.out.println("Hello World");
 } 
 

В таких случаях все потоки, которые используют предложение synchronized в классе SomeClass , должны будут синхронизироваться друг с другом.

Можно также использовать предложение synchronized для методов (например, public synchronized void method2() ); для нестатических методов синхронизируемым объектом будет объект, которому принадлежит этот метод, тогда как для статических методов (например, public static synchronized void method1() ) будет сам класс, к которому принадлежит этот метод.