Почему фиктивный объект передается в качестве аргумента для синхронизации на уровне блоков? - PullRequest
2 голосов
/ 01 февраля 2012

Нашел этот код в источнике Spring.Это первый шаг, когда XML-файл преобразуется в Bean Tree.

/** Synchronization monitor for the "refresh" and "destroy" */
    private final Object startupShutdownMonitor = new Object();

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
}

Ответы [ 6 ]

7 голосов
/ 01 февраля 2012

Эта идиома используется для более точной синхронизации уровня. Вот выдержка из учебника Java . Вы могли бы использовать synchronized(this), но это заблокировало бы весь объект .

Синхронизированные операторы также полезны для улучшения параллелизма с мелкозернистая синхронизация. Предположим, например, что класс MsLunch имеет два поля экземпляра, c1 и c2, которые никогда не используются вместе. Все обновления этих полей должны быть синхронизированы, но нет причин предотвращение чередования обновления c1 с обновлением c2 - и это уменьшает параллелизм за счет создания ненужных блокировок. Вместо использования синхронизированных методов или иного использования блокировки В связи с этим мы создаем два объекта исключительно для обеспечения блокировки.

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

Начиная с Java 5, Java представила Lock абстракцию, обеспечивающую больше функциональности. Таким образом, вместо synchronized (obj) вы можете сделать что-то вроде ниже. Подробнее подробнее здесь

Lock lock = new ReentrantLock();

lock.lock();

c1++;

lock.unlock();
1 голос
/ 01 февраля 2012

Полагаю, что вам принципиально интересно, почему они не использовали synchronized(this)?Существует две причины, по которым код, который вы разместили, может быть лучше:

  1. Безопасность (или, скорее, уверенность в поведении).Поскольку другие классы могут иметь ссылку на экземпляр вашего класса, они также могут синхронизировать его.(Фактически, каждый раз, когда вы не синхронизируете на this, вы почти наверняка синхронизируете на мониторе другого класса).Вы можете написать свой код так, чтобы он работал автономно, но создавал взаимоблокировки, если какой-то другой код, использующий его, синхронизировался определенным образом.Синхронизация на this фактически общедоступна, но я сомневаюсь, что вы обычно ее документируете (или что другой разработчик прочтет эту документацию).Синхронизируя с таким частным конечным полем, вы можете гарантировать, что никакой другой код не сможет синхронизироваться с одним и тем же объектом, что значительно упростит логику того, что вам нужно защищать.
  2. Несколько операций в одном классе.Если у вас есть два отдельных счетчика, например, и вы используете синхронизацию для предотвращения потерянных обновлений - нет причины, по которой запись в счетчик 1 должна блокировать чтение счетчика 2. Если все синхронизируется на this, то толькоодин из этих методов может произойти одновременно.Синхронизация определенных объектов позволяет создавать группы методов / блоков, которые являются взаимоисключающими внутри группы, но не препятствуют выполнению блоков из другой группы.

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

В настоящее время, если я синхронизируюсь, я 'Я всегда буду делать это в определенном поле new Object(), даже если на данный момент это всего лишь одна операция.

1 голос
/ 01 февраля 2012

Я не могу говорить конкретно за авторов Spring, но в целом ...

Это сделано для того, чтобы код был синхронизирован (очевидно), но намеренно не синхронизирован на this.

Зачем нам это делать?Есть несколько причин

  1. В классе есть другие синхронизированные блоки, но между этими блоками и этим нет взаимозависимостей.Поэтому они не должны синхронизироваться вокруг одной и той же блокировки - каждый ресурс синхронизируется вокруг другого объекта.
  2. Вы хотите скрыть блокировку от пользователей вашего класса.Всегда есть риск, что один из ваших пользователей может решить сделать synchronized(theObject) и в конечном итоге использовать тот же объект, что и блокировку за пределами вашего класса, что вы использовали внутри своего класса.В некоторых случаях это может вызвать серьезные проблемы с производительностью / параллелизмом

Почему Object?Потому что это все, что нужно для того, чтобы получить блокировку, а все остальное может привести к дополнительным расходам.

0 голосов
/ 13 января 2014

Если часть кода, имеющая определенные переменные класса, полностью независима от других, то вместо получения блокировки всего объекта с помощью synchronize(this) лучше использовать блокировку фиктивного объекта.Код внутри все еще защищен, потому что монитор теперь находится с фиктивной блокировкой объекта.

0 голосов
/ 01 февраля 2012

Ах, я видел это раньше :) Я на самом деле обдумывал то же самое.

Это из AbstractApplicationContext, если я правильно помню.

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

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

Если я правильно помню, в этом же классе есть еще один монитор, называемый activeMonitor.

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

это позволяет защитить его от утечки монитора (который не представляет интереса для кого-либо за пределами его класса) другим объектам, которые могут злоупотреблять им (поместить его всостояние вечных чур), таким образом балуясь в процессе.

0 голосов
/ 01 февраля 2012
private final Object startupShutdownMonitor = new Object();

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
       // Prepare this context for refreshing.
       prepareRefresh();
   }

}

Это эквивалентно следующему

public void refresh() throws BeansException, IllegalStateException {
      //Do something that does not affect the state of the object
      System.out.println("I am inside the refresh method() and will accquire lock on the object now");
      prepareRefresh();
}

private synchronized void preparedRefresh() {
    //Do something thread safe here
    //Since the thread here has the monitor it can safely alter the state of the class instance here with causing inconsistensy
}

Получение блокировки на объекте экземпляра startupShutdownMonitor аналогично получению блокировки на экземпляре класса, для которого вызывается метод обновленияна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...