О вызове методов из синхронизированного блока

#java #multithreading #java-8 #synchronization #thread-safety

#java #многопоточность #java-8 #синхронизация #безопасность потоков

Вопрос:

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

Например:

 public synchronized void foo(){

    if(critical condition){
        bar();  // can influence the above condition
    }
    baz(); // can influence the above condition
}
 

Могут ли быть включены два потока bar (предположим, что он вызывается только отсюда)?

Что, если baz может быть вызвано из другого места в коде, кроме foo , тогда в нем могут быть два потока?

Ответ №1:

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

Да, при условии, что они используют разные объекты или один из них используется wait() .

Что, если baz может быть вызван из другого места в коде, отличного от foo, могут ли тогда в нем быть два потока?

Да, размещение synchronized на одном методе не влияет на методы, которые не синхронизированы.

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

1. Под другим объектом вы подразумеваете другой экземпляр класса foo ? Кроме того, будет ли установка блокировки foo делать только один поток в bar or baz ?

2. @kuhaku у вас может быть несколько экземпляров класса, который реализует foo() , если у вас есть только один экземпляр и только один метод, который может вызывать bar() , и baz() тогда в этих методах может быть только один поток (если вы не используете wait())

3. Может вызываться более одного метода baz() , и существует несколько экземпляров включающего класса, но они действуют сами по себе, а не друг на друга…

4. @kuhaku в этом случае любое количество потоков может запускать этот метод. Представьте, что у вас есть два города, каждый с неприступными воротами, но без стен. Могут ли два человека попасть в город одновременно?

5. @kuhaku У каждого объекта есть одна блокировка (если только это не a Lock ) если у вас есть два экземпляра, два потока могут удерживать блокировку для каждого из них и находиться в одном и том же методе (но с другим экземпляром) Важно то, как объекты заблокированы или нет. Вы не блокируете методы. Блокировка объекта не влияет на код, который не использует synchronized.

Ответ №2:

Это эквивалентный способ написания вашего кода:

 public void foo(){
    synchronized (this) {
        if(critical condition){
            bar();  // can influence the above condition
        }
        baz(); // can influence the above condition
    }
}
 

Потому что синхронизированные методы фактически синхронизируют свои тела, используя this их в качестве объекта блокировки.

Итак, могут ли два потока выполняться foo() одновременно или bar() в одно и то же время? Конечно, если они выполняют foo() или bar() разных объектов.

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

Эти два ресурса полезны для понимания того, что синхронизация делает и не делает в Java:

Я рекомендую вам проверить эти страницы, потому что есть некоторые фрагменты информации, которые не слишком очевидны, пока вы их не проверите. Например:

  • Два разных потока не могут выполнять одновременно два разных синхронизированных метода одного объекта.
  • Один поток может выполнять два разных синхронизированных метода одного и того же объекта (так называемая реентерабельная синхронизация).

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

1. Итак, похоже, что если я синхронизирую bar() и baz() , один поток возьмет this в качестве блокировки и будет использовать его через оба bar , baz до конца foo и только один поток?

2. Я видел эти ссылки до того, как задал вопрос, в них нет примера вызова метода из блока синхронизации…

3. Блокировка, которую поток получает при входе foo , останется с потоком до выхода foo , даже если внутри foo вы вызываете другие методы. Пока поток находится внутри bar , он все еще сохраняет блокировку, потому foo что все еще находится в процессе выполнения.

4. Понял, и я мог бы просто синхронизировать baz , чтобы другой поток не входил в него одновременно.

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