在内部同步内部或外部通知侦听器

时间:2010-06-17 13:12:31

标签: api synchronization thread-safety listener

我正在努力做出决定。我正在编写一个线程安全的库/ 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 个答案:

答案 0 :(得分:1)

这取决于您在方法中获取侦听器的位置,您拥有的侦听器数量,侦听器如何订阅/取消订阅

假设从您的示例中,您只有一个侦听器,那么您可能最好将关键部分(或监视器)用于类的不同部分,而不是锁定整个对象。

你可以有一个锁用于在方法中执行特定于手头的对象/任务的任务,一个用于监听器subscribe / unsubscribe / notify(即确保在通知期间不更改监听器)

我还会使用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();