我用一些「可觀察」屬性定義了一個類。該類內部包含一個執行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