回首Java Beans
我们此刻已领略了同步,接着可换从另一个角度来考查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); } } ///:~
#p#分页标题#e#
很容易就可觉得要领添加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的多造型本领。