уведомить слушателя внутри или снаружи внутренней синхронизации - PullRequest
5 голосов
/ 17 июня 2010

Я борюсь с решением.Я пишу потокобезопасную библиотеку / API.Слушатели могут быть зарегистрированы, поэтому клиент получает уведомление, когда происходит что-то интересное.Какая из двух реализаций наиболее распространена?

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        synchronized(this) {
             ... do useful stuff ...
             listener.notify();
        }
    }
}

или

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        Listener l = null;

        synchronized(this) {
             ... do useful stuff ...
             l = listener;
        }
        l.notify();
    }
}

В первой реализации слушатель уведомляется внутри синхронизации.Во второй реализации это делается вне синхронизации.

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

Недостатком второй реализации является то, что клиент может получать «неправильные» уведомления, что происходит, если он получил доступ и изменил модуль до оператора l.notify (),Например, если он попросил модуль прекратить отправку уведомлений, это уведомление отправляется в любом случае.Это не относится к первой реализации.

большое спасибо

1 Ответ

1 голос
/ 17 июня 2010

Это зависит от того, где вы получаете слушателя в вашем методе, сколько у вас слушателей, как подписчик подписывается / отписывается

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

У вас может быть одна блокировка для выполнения задач в рамках метода, специфичного для данного объекта / задачи, и одна для слушателя «подписаться / отписаться / уведомить» (это означает, что слушатель не изменяется во время уведомления) .

Я бы также использовал ReadWriteLock, защищающий ссылки на слушателей (один или список слушателей)

Отвечая на ваш комментарий:

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

Уведомление слушателя (если оно защищено, как я описал) не должно задерживать какой-либо другой поток, требующий средств класса. Наилучшей стратегией является создание блокировок, специфичных для состояния класса, и блокировок, специфичных для безопасного уведомления.

Если вы воспользуетесь своим примером приостановки уведомлений, это может быть закрыто блокировкой, которая управляет уведомлениями, поэтому, если другой поток «приостанавливает» уведомления, либо приостановится обработка, либо текущее уведомление завершится, если другой поток приостановит уведомление между обрабатываемой задачей и полученным уведомлением l.notify () не произойдет.

Listener l = null;

synchronised(processLock_) {
   ... do stuff....
   synchronised(notifyLock_) {
      l = listener;
   }
} 
//
// current thread preempted by other thread that suspends notification here.
//

synchronised(notifyLock_) { // ideally use a readwritelock here...
   l = allowNotify_ ? l: null;
}
if(l)
   l.notify();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...