我定义了一个具有许多“可观察”属性的类。在内部,类包含一个执行I/O的线程。
public class Foo {
private final PropertyChangeSupport support;
private State state;
public Foo() { this.support = new PropertyChangeSupport(this); }
public synchronized State getState() { return state; }
public synchronized void setState(State state) {
if (this.state != state) {
State oldState = this.state;
this.state = state;
// Fire property change *whilst still holding the lock*.
support.firePropertyChange("state", oldState, state);
}
}
public synchronized void start() {
// Start I/O Thread, which will call setState(State) in some circumstances.
new Thread(new Runnable() ...
}
}我的问题是:我应该避免在持有类锁的同时触发属性更改事件吗?或者我应该从一个专用线程(例如“事件-多播”线程)触发属性更改事件?
当前的设计导致了一个死锁,线程A在这个死锁中取出外部类:Bar上的一个锁,然后尝试调用Foo上的一个方法并取出第二个锁。但是,同时I/O线程调用获取Foo上的锁的Foo,它将一个属性更改事件传播到包含的类Bar,并尝试获取该类上的锁.导致僵局。换句话说,属性更改回调设计意味着我无法有效地控制获取锁的顺序。
我目前的解决方法是使状态volatile并删除synchronized关键字,但这似乎是一种套话;首先,这意味着不能保证触发属性更改事件的顺序。
发布于 2009-09-24 11:14:59
如果您需要让其他线程从notification循环中回调到您的类中,那么您需要减少同步的范围,使用同步块而不是同步整个消息(这是从您的帖子中复制的,不知道它是否编译):
public void setState(State state) {
State oldState = null;
synchronized (this) {
if (this.state != state) {
oldState = this.state;
this.state = state;
}
}
if (oldState != null)
support.firePropertyChange("state", oldState, state);
}尽可能短的时间内持有锁。并考虑用同步消息队列替换这些回调(查看java.util.concurrent)。
编辑,对答案进行概括,并添加一些哲学。
首先,多线程编程是很难的。任何对你有不同看法的人都想卖给你一些东西。在多线程程序中创建许多相互依赖的连接会导致微妙的错误,如果不是完全疯狂的话。
我所知道的简化多线程编程的最好方法是编写具有定义良好的起始点和停止点的独立模块--换句话说,就是演员模型。单个演员是单线程的,您可以很容易地单独理解和测试它。
纯参与者模型与事件通知非常匹配:响应事件调用该参与者,并执行一些操作。它不在乎另一个事件是否已经启动。有时,这还不够:您需要根据由另一个线程管理的状态做出决定。没关系:演员可以(以同步的方式)查看该状态并做出决定。
要记住的关键是(汤姆·霍廷在他的评论中指出),你现在读到的状态可能与现在有一毫秒的不同。您永远不能编写假定您知道对象的确切状态的代码。如果你觉得你需要这样做,你需要重新考虑你的设计。
最后一条评论: Doug比你或我聪明,不要试图重新发明java.util.concurrent中的类。
https://stackoverflow.com/questions/1470992
复制相似问题