程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 回顧Java Beans

回顧Java Beans

編輯:關於JAVA

我們現在已理解了同步,接著可換從另一個角度來考察Java Beans。無論什麼時候創建了一個Bean,就必須假定它要在一個多線程的環境中運行。這意味著:
(1) 只要可行,Bean的所有公共方法都應同步。當然,這也帶來了“同步”在運行期間的開銷。若特別在意這個問題,在關鍵區域中不會造成問題的方法就可保留為“不同步”,但注意這通常都不是十分容易判斷。有資格的方法傾向於規模很小(如下例的getCircleSize())以及/或者“微小”。也就是說,這個方法調用在如此少的代碼片裡執行,以至於在執行期間對象不能改變。如果將這種方法設為“不同步”,可能對程序的執行速度不會有明顯的影響。可能也將一個Bean的所有public方法都設為synchronized,並只有在保證特別必要、而且會造成一個差異的情況下,才將synchronized關鍵字刪去。
(2) 如果將一個多造型事件送給一系列對那個事件感興趣的“聽眾”,必須假在列表中移動的時候可以添加或者刪除。

第一點很容易處理,但第二點需要考慮更多的東西。讓我們以前一章提供的BangBean.java為例。在那個例子中,我們忽略了synchronized關鍵字(那時還沒有引入呢),並將造型設為單造型,從而回避了多線程的問題。在下面這個修改過的版本中,我們使其能在多線程環境中工作,並為事件采用了多造型技術:
 

//: BangBean2.java
// You should write your Beans this way so they 
// can run in a multithreaded environment.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;

public class BangBean2 extends Canvas 
    implements Serializable {
  private int xm, ym;
  private int cSize = 20; // Circle size
  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());
  }
  public synchronized int getCircleSize() { 
    return cSize; 
  }
  public synchronized void 
  setCircleSize(int newSize) {
    cSize = newSize;
  }
  public synchronized String getBangText() { 
    return text; 
  }
  public synchronized void 
  setBangText(String newText) {
    text = newText;
  }
  public synchronized int getFontSize() { 
    return fontSize; 
  }
  public synchronized void 
  setFontSize(int newSize) {
    fontSize = newSize;
  }
  public synchronized Color getTextColor() {
    return tColor; 
  }
  public synchronized void 
  setTextColor(Color newColor) {
    tColor = newColor;
  }
  public void paint(Graphics g) {
    g.setColor(Color.black);
    g.drawOval(xm - cSize/2, ym - cSize/2, 
      cSize, cSize);
  }
  // This is a multicast listener, which is
  // more typically used than the unicast
  // approach taken in BangBean.java:
  public synchronized void addActionListener (
      ActionListener l) {
    actionListeners.addElement(l);
  }
  public synchronized void removeActionListener(
      ActionListener l) {
    actionListeners.removeElement(l);
  }
  // Notice this isn't synchronized:
  public void notifyListeners() {
    ActionEvent a =
      new ActionEvent(BangBean2.this,
        ActionEvent.ACTION_PERFORMED, null);
    Vector lv = null;
    // Make a copy of the vector in case someone
    // adds a listener while we're 
    // calling listeners:
    synchronized(this) {
      lv = (Vector)actionListeners.clone();
    }
    // Call all the listener methods:
    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();
    }
  }
  // Testing the BangBean2:
  public static void main(String[] args) {
    BangBean2 bb = new BangBean2();
    bb.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e){
        System.out.println("ActionEvent" + e);
      }
    });
    bb.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e){
        System.out.println("BangBean2 action");
      }
    });
    bb.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e){
        System.out.println("More action");
      }
    });
    Frame aFrame = new Frame("BangBean2 Test");
    aFrame.addWindowListener(new WindowAdapter(){
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    aFrame.add(bb, BorderLayout.CENTER);
    aFrame.setSize(300,300);
    aFrame.setVisible(true);
  }
} ///:~

很容易就可以為方法添加synchronized。但注意在addActionListener()和removeActionListener()中,現在添加了ActionListener,並從一個Vector中移去,所以能夠根據自己願望使用任意多個。
我們注意到,notifyListeners()方法並未設為“同步”。可從多個線程中發出對這個方法的調用。另外,在對notifyListeners()調用的中途,也可能發出對addActionListener()和removeActionListener()的調用。這顯然會造成問題,因為它否定了Vector actionListeners。為緩解這個問題,我們在一個synchronized從句中“克隆”了Vector,並對克隆進行了否定。這樣便可在不影響notifyListeners()的前提下,對Vector進行操縱。
paint()方法也沒有設為“同步”。與單純地添加自己的方法相比,決定是否對過載的方法進行同步要困難得多。在這個例子中,無論paint()是否“同步”,它似乎都能正常地工作。但必須考慮的問題包括:
(1) 方法會在對象內部修改“關鍵”變量的狀態嗎?為判斷一個變量是否“關鍵”,必須知道它是否會被程序中的其他線程讀取或設置(就目前的情況看,讀取或設置幾乎肯定是通過“同步”方法進行的,所以可以只對它們進行檢查)。對paint()的情況來說,不會發生任何修改。
(2) 方法要以這些“關鍵”變量的狀態為基礎嗎?如果一個“同步”方法修改了一個變量,而我們的方法要用到這個變量,那麼一般都願意把自己的方法也設為“同步”。基於這一前提,大家可觀察到cSize由“同步”方法進行了修改,所以paint()應當是“同步”的。但在這裡,我們可以問:“假如cSize在paint()執行期間發生了變化,會發生的最糟糕的事情是什麼呢?”如果發現情況不算太壞,而且僅僅是暫時的效果,那麼最好保持paint()的“不同步”狀態,以避免同步方法調用帶來的額外開銷。
(3) 要留意的第三條線索是paint()基礎類版本是否“同步”,在這裡它不是同步的。這並不是一個非常嚴格的參數,僅僅是一條“線索”。比如在目前的情況下,通過同步方法(好cSize)改變的一個字段已合成到paint()公式裡,而且可能已改變了情況。但請注意,synchronized不能繼承——也就是說,假如一個方法在基礎類中是“同步”的,那麼在衍生類過載版本中,它不會自動進入“同步”狀態。
TestBangBean2中的測試代碼已在前一章的基礎上進行了修改,已在其中加入了額外的“聽眾”,從而演示了BangBean2的多造型能力。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved