Почему нужно указывать объект? Ведь мы можем вообще не использовать этот объект в блоке.
Также чем отличается synchronized(this) от synchronized(o)(я знаю что значит this, чем отличаются блоки с разными объектами)?
И наконец, почему мы можем использовать операции wait, notify и т.п. с Object o, ведь он не поток?
Ключевое слово synchronized является утверждением, которое в круглых скобках принимает выражение, возвращающее ссылку на объект. Поэтому его не нужно путать с методом, принимающим один параметр. И то, что используется в скобках - относится к самому утверждению, а не блоку кода, для которого это утверждение работает. Как и в любом другом блоке кода вы можете использовать ссылку на любой объект, если он доступен в области видимости, которая охватывает этот блок. Поэтому какую ссылку вы используете в утверждении synchronized, не имеет отношения к тому, будете ли вы ее использовать в блоке кода или нет.
Вот, что по этому сказано в JLS:
Утверждение synchronized получает блокировку взаимного исключения
(§17.1) от имени исполняющего потока, выполняет блок, а затем
освобождает блокировку. Пока исполняющий поток владеет блокировкой, ни
один другой поток не может получить блокировку.
SynchronizedStatement:
synchronized ( Expression ) Block
Тип выражения должен быть ссылочным типом, или возникает ошибка времени компиляции.
И наконец, методы wait(), notify(), и notifyAll() принадлежат классу Object. Поэтому вы можете использовать их в любом классе, вы также можете использовать ссылку на объект, для вызовов этих методов из этого объекта, имеющего эту ссылку. Так как впрочем все унаследованные методы доступны в классе без использования ссылки. Ссылка this указывает на этот объект, поэтому ее указывать необязательно для вызовов наследуемых методов, в то время как для других объектов это необходимо. Поток тоже является объектом, потому, что создается из класса Thread, поэтому если методы вызываются внутри класса, то принадлежат ему.
Также чем отличается synchronized(this) от synchronized(o)
ничем, все зависит от того что вы будете делать с объектом который по которому синхронизировались, возможно вы захотите передать его в метод или предоставить доступ к нему из вне.
И наконец, почему мы можем использовать операции wait, notify и т.п. с Object o, ведь он не поток?
Вот здесь у вас небольшой пробел в знаниях, вам необходимо почитать про понятие монитора. Вкратце у каждого объекта есть монитор, можете представлять его как флаг, когда вы пишете
synchronized(this) {...}
поток который заходит внуть этого блока захватывает монитор объекта this, таким образом другие потоки видят что монитор захвачен и не заходят внутрь.
Если рассматривать оператор
synchronized (ссылка_ на_ объект) {
// блок кода, который нужно синхронизировать
}
то здесь ссылка_ на_ объект обозначает ссылку на синхронизируемый объект.
Блок оператора synchronized гарантирует, что вызов метода, являющегося
членом того же класса, что и синхронизируемый объект, на который делается
ссылка_ на_ объект, произойдет только тогда, когда текущий поток исполнения
успешно войдет в монитор данного объекта.
В синхронизируемом блоке можно не объявлять оператор synchronized со ссылкой на объект тогда, когда вы точно используете метод, объявленный как synchronized. Если вы не имеете исходника, чтобы пере-объявить этот метод, то используете оператор synchronized (ссылка_ на_ объект). Какой объект указывать - this или o - зависит от вашей программы. Если синхронизируется блок кода из текущего объекта, то он уже создан и ссылкой на него является this. Если этот метод в другом объекте, то его и указываете. Как-то так...
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости