Синхронизация дважды на одном и том же объекте? - PullRequest
46 голосов
/ 30 октября 2008

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

Сценарий выглядит следующим образом

pulbic class SillyClassName {

    object moo;
    ...
    public void method1(){
        synchronized(moo)
        {
            ....
            method2();
            ....
        }
    }

    public void method2(){
        synchronized(moo)
        {
            doStuff();
        }
    }
}

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

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

Ответы [ 6 ]

67 голосов
/ 30 октября 2008

1001 * Повторный вход * Синхронизированные блоки используют блокировки reentrant , что означает, что, если поток уже удерживает блокировку, он может без проблем повторно получить ее. Поэтому ваш код будет работать так, как вы ожидаете. См. В нижней части Учебное пособие по Java страница Внутренние блокировки и синхронизация . Цитировать по состоянию на 2015-01… Входящая синхронизация Напомним, что поток не может получить блокировку, принадлежащую другому потоку. Но поток может получить блокировку, которой он уже владеет. Разрешение потоку получить одну и ту же блокировку более одного раза включает повторную входящую синхронизацию . Это описывает ситуацию, когда синхронизированный код прямо или косвенно вызывает метод, который также содержит синхронизированный код, и оба набора кода используют одну и ту же блокировку. Без повторной входящей синхронизации синхронизированный код должен был бы принять множество дополнительных мер предосторожности, чтобы избежать блокировки потока самим потоком.

4 голосов
/ 05 апреля 2012

Я думаю, что мы должны использовать блокировку повторного входа для того, что вы пытаетесь сделать. Вот фрагмент из http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html.

Что мы подразумеваем под входящим замком? Просто то, что есть счетчик обнаружений, связанный с блокировкой, и если поток, который удерживает блокировку, получает его снова, счетчик захватов увеличивается, и затем блокировка должна быть снята дважды, чтобы действительно снять блокировку. Это соответствует семантике синхронизированных; если поток входит в синхронизированный блок, защищенный монитором, которому этот поток уже принадлежит, потоку будет разрешено продолжить, и блокировка не будет снята, когда поток выйдет из второго (или последующего) синхронизированного блока, но будет освобождена только когда он выходит из первого синхронизированного блока, в который он вошел, защищенный этим монитором.

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

2 голосов
/ 11 ноября 2011

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

Мое тестирование было сделано под Java 6 SE.

1 голос
/ 30 октября 2008

Нет проблем. В вашем примере (когда вы исправите свой код, чтобы избавиться от предупреждений компиляции, которые вы получите;)), синхронизация гарантирует, что блоки в method1 и method2 не будут выполняться одновременно.

Это своего рода точка синхронизации. :)


Редактировать: Извините, пропустили части вашего вопроса, но Фил ответил на него. Подводя итог, можно сказать, что один поток не может заблокировать сам себя.

0 голосов
/ 30 октября 2008

Нет, второй метод не остановится, если вызывается первым. Никаких странных результатов не возникнет (за исключением незначительных накладных расходов на проверку блокировки. Это не имеет большого значения. В Java 6 и более, у вас есть огрубление блокировки в JVM - http://java.sun.com/performance/reference/whitepapers/6_performance.html)

Например, взгляните на исходный код java.util.Vector. Существует много вызовов других синхронизированных методов из синхронизированных методов.

0 голосов
/ 30 октября 2008

В java ключевое слово synchronized для метода в основном синхронизируется с текущим объектом, поэтому фактически он делает то, что вы предлагаете, неявно.

У вас не возникнет проблем с синхронизацией одного объекта в одном методе и последующей синхронизацией на том же объекте в другом методе, поскольку, как вы говорите, текущий поток уже удерживает блокировку этого объекта.

...