我用一些“可观察”属性定义了一个类。该类内部包含一个执行I/O的单个线程;例如使用PropertyChangeListener锁定策略
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() ...
}
}
我的问题是:我应该避免在保持课堂锁定的情况下解雇属性更改事件吗? ... 或也许我应该从单个专用线程(例如“event-multicaster”线程)触发属性更改事件?
当前的设计会导致死锁,即线程A取出外部类的锁:Bar
,然后尝试调用Foo
上的方法并取出第二个锁。但是,I/O线程同时调用setState(State)
获取Foo
上的锁定,该锁定将属性更改事件传播到包含类Bar
,并尝试获取该类上的锁定......导致死锁。换句话说,属性更改回调设计意味着我无法有效控制获取锁的顺序。
我目前的解决方法是使状态volatile
和删除关键字,但这似乎是一个kludge;一方面它意味着财产变更事件被解雇的顺序不能得到保证。
这绝对是一种改进,但不能保证以正确的顺序触发属性更改事件。 – Adamski 2009-09-24 11:30:20
欢迎来到多线程编程。这就是为什么我说更好的方法是使用消息队列作为中介。 – kdgregory 2009-09-24 11:39:02
谢谢 - 我明白了你的观点。我想这与我提到有一个专用的线程来触发PropertyChangeEvents。专用线程可以从队列中读取并传播,因此API不会更改。这也会更好,因为我的I/O线程从来没有冒险过我的班级......但没有那么好,当收到一个事件时,不能保证变量仍然具有该值:-( – Adamski 2009-09-24 12:07:33