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

Jar*_*els 5 api synchronization listener thread-safety

我正在努力做出决定.我正在编写一个线程安全的库/ API.可以注册监听器,以便在发生有趣的事情时通知客户端.这两种实现中哪一种最常见?

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        synchronized(this) {
             ... do useful stuff ...
             listener.notify();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

class MyModule {
   protected Listener listener; 

   protected void somethingHappens() {
        Listener l = null;

        synchronized(this) {
             ... do useful stuff ...
             l = listener;
        }
        l.notify();
    }
}
Run Code Online (Sandbox Code Playgroud)

在第一个实现中,在同步内通知侦听器.在第二种实现中,这是在同步之外完成的.

我觉得建议使用第二个,因为它减少了潜在的死锁空间.但我很难说服自己.

第二个实现的缺点是客户端可能会收到"不正确"的通知,如果它在l.notify()语句之前访问并更改了模块,则会发生这种情况.例如,如果它要求模块停止发送通知,则无论如何都会发送此通知.在第一个实现中不是这种情况.

非常感谢

Adr*_*gan 1

这取决于您在方法中的何处获取侦听器、您有多少个侦听器、侦听器如何订阅/取消订阅

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

您可以拥有一把锁,用于在方法内执行特定于当前对象/任务的任务,并拥有一把锁用于侦听器订阅/取消订阅/通知(即确保侦听器在通知期间不会更改)。

我还会使用 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();
Run Code Online (Sandbox Code Playgroud)