#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
orbaz
?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
.