首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

重訪 Java Beans:多執行緒環境下的同步與多播事件設計

     編輯:Java 相關知識

Bean 多執行緒設計兩大原則

建立 Bean 時必須假定它在多執行緒環境運行:

  1. 所有 public 方法盡可能同步: 雖帶來運行期開銷。若特別在意效能,關鍵區域不會出問題的方法可保留「不同步」,但通常難判斷。合格方法傾向規模很小(如 getCircleSize())或「微小」(執行代碼極少,執行期間物件狀態不變)。建議:所有 public 方法設 synchronized,僅在確保必要且有差異時移除
  2. 多播事件傳遞時假設監聽器列表可動態增減: 需更多考量

BangBean2:多執行緒安全的多播 Bean 實作

//: BangBean2.java
public class BangBean2 extends Canvas implements Serializable {
    private int xm, ym;
    private int cSize = 20;
    private String text = "Bang!";
    private int fontSize = 48;
    private Color tColor = Color.red;
    private Vector actionListeners = new Vector(); // 支援多播
    
    public BangBean2() {
        addMouseListener(new ML());
        addMouseMotionListener(new MM());
    }
    
    // 所有 getter/setter 同步
    public synchronized int getCircleSize() { return cSize; }
    public synchronized void setCircleSize(int newSize) { cSize = newSize; }
    // ... 其他屬性同步存取器 ...
    
    public void paint(Graphics g) {
        g.setColor(Color.black);
        g.drawOval(xm - cSize/2, ym - cSize/2, cSize, cSize);
    }
    
    // 多播監聽器管理:同步
    public synchronized void addActionListener(ActionListener l) { actionListeners.addElement(l); }
    public synchronized void removeActionListener(ActionListener l) { actionListeners.removeElement(l); }
    
    // 通知監聽器:故意不加 synchronized
    public void notifyListeners() {
        ActionEvent a = new ActionEvent(BangBean2.this, ActionEvent.ACTION_PERFORMED, null);
        Vector lv = null;
        synchronized(this) { lv = (Vector)actionListeners.clone(); } // 克隆快照
        for(int i = 0; i < lv.size(); i++) {
            ActionListener al = (ActionListener)lv.elementAt(i);
            al.actionPerformed(a);
        }
    }
    
    class ML extends MouseAdapter {
        public void mousePressed(MouseEvent e) {
            Graphics g = getGraphics();
            g.setColor(tColor);
            g.setFont(new Font("TimesRoman", Font.BOLD, fontSize));
            int width = g.getFontMetrics().stringWidth(text);
            g.drawString(text, (getSize().width - width) /2, getSize().height/2);
            g.dispose();
            notifyListeners();
        }
    }
    class MM extends MouseMotionAdapter {
        public void mouseMoved(MouseEvent e) { xm = e.getX(); ym = e.getY(); repaint(); }
    }
    // 測試主程式...
} ///:~

關鍵設計解析

1. notifyListeners() 不同步的巧妙處理

  • 可能從多執行緒呼叫
  • 調用途中可能增減監聽器 → 否決 actionListeners
  • **解法:同步區塊中 clone() 向量**,對克隆體迭代 → 原向量可安全操作

2. paint() 不同步的判斷依據

  1. 方法是否修改物件內部「關鍵」變數狀態?paint() 不修改
  2. 方法是否依賴「關鍵」變數狀態?cSize 由同步方法修改,paint() 用到 → 應同步。但問:**最壞情況是什麼?** 僅暫時效果 → 保持不同步避免開銷
  3. 基礎類版本是否同步?Canvas.paint() 不同步(僅作線索,synchronized 不可繼承)

測試代碼驗證多播能力

在 main() 中添加三個 ActionListener,演示多播事件分發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved