针对用户界面的多线程
此刻,我们也许能用一个线程办理在Counter1.java中呈现的问题。回收的一个能力即是在一个线程的run()要领中安排“子任务”——亦即位于go()内的轮回。一旦用户按下Start按钮,线程就会启动,但顿时竣事线程的建设。这样一来,尽量线程仍在运行,但措施的主要事情却能得以继承(等待并响应用户界面的事件)。下面是详细的代码:
//: Counter2.java // A responsive user interface with threads import java.awt.*; import java.awt.event.*; import java.applet.*; class SeparateSubTask extends Thread { private int count = 0; private Counter2 c2; private boolean runFlag = true; public SeparateSubTask(Counter2 c2) { this.c2 = c2; start(); } public void invertFlag() { runFlag = !runFlag;} public void run() { while (true) { try { sleep(100); } catch (InterruptedException e){} if(runFlag) c2.t.setText(Integer.toString(count++)); } } } public class Counter2 extends Applet { TextField t = new TextField(10); private SeparateSubTask sp = null; private Button onOff = new Button("Toggle"), start = new Button("Start"); public void init() { add(t); start.addActionListener(new StartL()); add(start); onOff.addActionListener(new OnOffL()); add(onOff); } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp == null) sp = new SeparateSubTask(Counter2.this); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp != null) sp.invertFlag(); } } public static void main(String[] args) { Counter2 applet = new Counter2(); Frame aFrame = new Frame("Counter2"); aFrame.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); aFrame.add(applet, BorderLayout.CENTER); aFrame.setSize(300,200); applet.init(); applet.start(); aFrame.setVisible(true); } } ///:~
此刻,Counter2酿成了一个相当直接的措施,它的独一任务就是配置并打点用户界面。但假利用户此刻按下Start按钮,却不会真正挪用一个要领。此时不是建设类的一个线程,而是建设SeparateSubTask,然后继承Counter2事件轮回。留意此时会生存SeparateSubTask的句柄,以便我们按下onOff按钮的时候,能正常地切换位于SeparateSubTask内部的runFlag(运行符号)。随后谁人线程便可启动(当它看到符号的时候),然后将本身中止(亦可将SeparateSubTask设为一个内部类来到达这一目标)。
SeparateSubTask类是对Thread的一个简朴扩展,它带有一个构建器(个中生存了Counter2句柄,然后通过挪用start()来运行线程)以及一个run()——本质上包括了Counter1.java的go()内的代码。由于SeparateSubTask知道本身容纳了指向一个Counter2的句柄,所以可以或许在需要的时候参与,并会见Counter2的TestField(文本字段)。
按下onOff按钮,险些当即能获得正确的响应。虽然,这个响应其实并不是“当即”产生的,它究竟和那种由“间断”驱动的系统差异。只有线程拥有CPU的执行时间,并留意到标志已产生改变,计数器才会遏制。
1. 用内部类改进代码
下面说说题外话,请各人留意一下SeparateSubTask和Counter2类之间产生的团结行为。SeparateSubTask同Counter2“亲密”地团结到了一起——它必需持有指向本身“父”Counter2工具的一个句柄,以便本身能回和谐哄骗它。但两个类并不是真的归并为单唯一个类(尽量在下一节中,我们会讲到Java确实提供了归并它们的要领),因为它们各自做的是差异的工作,并且是在差异的时间建设的。但不管奈何,它们依然细密地团结到一起(更精确地说,应该叫“连系”),所以使措施代码几多显得有些鸠拙。在这种环境下,一个内部类可以显著改进代码的“可读性”和执行效率:
//: Counter2i.java // Counter2 using an inner class for the thread import java.awt.*; import java.awt.event.*; import java.applet.*; public class Counter2i extends Applet { private class SeparateSubTask extends Thread { int count = 0; boolean runFlag = true; SeparateSubTask() { start(); } public void run() { while (true) { try { sleep(100); } catch (InterruptedException e){} if(runFlag) t.setText(Integer.toString(count++)); } } } private SeparateSubTask sp = null; private TextField t = new TextField(10); private Button onOff = new Button("Toggle"), start = new Button("Start"); public void init() { add(t); start.addActionListener(new StartL()); add(start); onOff.addActionListener(new OnOffL()); add(onOff); } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp == null) sp = new SeparateSubTask(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp != null) sp.runFlag = !sp.runFlag; // invertFlag(); } } public static void main(String[] args) { Counter2i applet = new Counter2i(); Frame aFrame = new Frame("Counter2i"); aFrame.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); aFrame.add(applet, BorderLayout.CENTER); aFrame.setSize(300,200); applet.init(); applet.start(); aFrame.setVisible(true); } } ///:~
#p#分页标题#e#
这个SeparateSubTask名字不会与前例中的SeparateSubTask斗嘴——纵然它们都在沟通的目次里——因为它已作为一个内部类埋没起来。各人亦可看到内部类被设为private(私有)属性,这意味着它的字段和要领都可得到默认的会见权限(run()除外,它必需设为public,因为它在基本类中是果真的)。除Counter2i之外,其他任何方面都不行会见private内部类。并且由于两个类细密团结在一起,所以很容易放宽它们之间的会见限制。在SeparateSubTask中,我们可看到invertFlag()要领已被删去,因为Counter2i此刻可以直接会见runFlag。
另外,留意SeparateSubTask的构建器已获得了简化——它此刻独一的用外就是启动线程。Counter2i工具的句柄仍象以前那样得以捕捉,但不再是通过人工通报和引用外部工具来到达这一目标,此时的内部类机制可以自动顾问它。在run()中,可看到对t的会见是直接举办的,好像它是SeparateSubTask的一个字段。父类中的t字段此刻可以酿成private,因为SeparateSubTask能在未获任何非凡许可的前提下自由地会见它——并且无论如何都该尽大概地把字段酿成“私有”属性,以防来自类外的某种气力不慎地改变它们。
无论在什么时候,只要留意到类彼此之间团结得较量细密,就可思量操作内部类来改进代码的编写与维护。