我们现在已理解了同步接着可换从另一个角度来考察Java Beans无论什么时候创建了一个Bean就必须假定它要在一个多线程的环境中运行这意味着
() 只要可行Bean的所有公共方法都应同步当然这也带来了同步在运行期间的开销若特别在意这个问题在关键区域中不会造成问题的方法就可保留为不同步但注意这通常都不是十分容易判断有资格的方法倾向于规模很小(如下例的getCircleSize())以及/或者微小也就是说这个方法调用在如此少的代码片里执行以至于在执行期间对象不能改变如果将这种方法设为不同步可能对程序的执行速度不会有明显的影响可能也将一个Bean的所有public方法都设为synchronized并只有在保证特别必要而且会造成一个差异的情况下才将synchronized关键字删去
() 如果将一个多造型事件送给一系列对那个事件感兴趣的听众必须假在列表中移动的时候可以添加或者删除
第一点很容易处理但第二点需要考虑更多的东西让我们以前一章提供的BangBeanjava为例在那个例子中我们忽略了synchronized关键字(那时还没有引入呢)并将造型设为单造型从而回避了多线程的问题在下面这个修改过的版本中我们使其能在多线程环境中工作并为事件采用了多造型技术
//: BangBeanjava
// You should write your Beans this way so they
// can run in a multithreaded environment
import javaawt*;
import javaawtevent*;
import javautil*;
import javaio*;
public class BangBean extends Canvas
implements Serializable {
private int xm ym;
private int cSize = ; // Circle size
private String text = Bang!;
private int fontSize = ;
private Color tColor = Colorred;
private Vector actionListeners = new Vector();
public BangBean() {
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) {
gsetColor(Colorblack);
gdrawOval(xm cSize/ ym cSize/
cSize cSize);
}
// This is a multicast listener which is
// more typically used than the unicast
// approach taken in BangBeanjava:
public synchronized void addActionListener (
ActionListener l) {
actionListenersaddElement(l);
}
public synchronized void removeActionListener(
ActionListener l) {
actionListenersremoveElement(l);
}
// Notice this isnt synchronized:
public void notifyListeners() {
ActionEvent a =
new ActionEvent(BangBeanthis
ActionEventACTION_PERFORMED null);
Vector lv = null;
// Make a copy of the vector in case someone
// adds a listener while were
// calling listeners:
synchronized(this) {
lv = (Vector)actionListenersclone();
}
// Call all the listener methods:
for(int i = ; 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。Tw.wiNgWIt.Com但注意在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的多造型能力。