2010-06-17 60 views
5

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

这取决于你在哪里让听者在你的方法,你有多少听众有,听者如何订阅/退订

从你的榜样假设,你只有一个监听器,然后你可能会更好地使用关键部分(或监视器)来处理类的不同部分,而不是锁定整个对象。

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

我也将使用一个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(); 
+0

感谢您的回答。我故意点没有提及订阅/取消订阅部分。我想假设有一个固定的听众。 我的问题是,如果在课堂外暴露同步是明智的。 – 2010-06-17 13:31:21

+0

@Jary Zeels,看到补充回答 – 2010-06-17 13:44:05

+0

感谢阿德里安的澄清,我现在明白了。有一位听众的情况对我来说很清楚。我会考虑更多听众可以注册的情况,因为那样的话,再entrancycan会成为问题。例如,某个通知的侦听器执行某些触发新通知的事件时,您不希望按错误顺序传送事件。我会考虑这个问题,如果需要的话可以回复更多的问题。再次感谢。 – 2010-06-17 14:12:03