现在我们也许能用一个线程解决在Counterjava中出现的问题采用的一个技巧便是在一个线程的run()方法中放置子任务——亦即位于go()内的循环一旦用户按下Start按钮线程就会启动但马上结束线程的创建这样一来尽管线程仍在运行但程序的主要工作却能得以继续(等候并响应用户界面的事件)下面是具体的代码 //: Counterjava // A responsive user interface with threads import javaawt*; import javaawtevent*; import javaapplet*; class SeparateSubTask extends Thread { private int count = ; private Counter c; private boolean runFlag = true; public SeparateSubTask(Counter c) { thisc = c; start(); } public void invertFlag() { runFlag = !runFlag;} public void run() { while (true) { try { sleep(); } catch (InterruptedException e){} if(runFlag) ctsetText(IntegertoString(count++)); } } } public class Counter extends Applet { TextField t = new TextField(); private SeparateSubTask sp = null; private Button onOff = new Button(Toggle) start = new Button(Start); public void init() { add(t); startaddActionListener(new StartL()); add(start); onOffaddActionListener(new OnOffL()); add(onOff); } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp == null) sp = new SeparateSubTask(Counterthis); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp != null) spinvertFlag(); } } public static void main(String[] args) { Counter applet = new Counter(); Frame aFrame = new Frame(Counter); aFrameaddWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { Systemexit(); } }); aFrameadd(applet BorderLayoutCENTER); aFramesetSize(); appletinit(); appletstart(); aFramesetVisible(true); } } 现在Counter变成了一个相当直接的程序它的唯一任务就是设置并管理用户界面但假若用户现在按下Start按钮却不会真正调用一个方法此时不是创建类的一个线程而是创建SeparateSubTask然后继续Counter事件循环注意此时会保存SeparateSubTask的句柄以便我们按下onOff按钮的时候能正常地切换位于SeparateSubTask内部的runFlag(运行标志)随后那个线程便可启动(当它看到标志的时候)然后将自己中止(亦可将SeparateSubTask设为一个内部类来达到这一目的) SeparateSubTask类是对Thread的一个简单扩展它带有一个构建器(其中保存了Counter句柄然后通过调用start()来运行线程)以及一个run()——本质上包含了Counterjava的go()内的代码由于SeparateSubTask知道自己容纳了指向一个Counter的句柄所以能够在需要的时候介入并访问Counter的TestField(文本字段) 按下onOff按钮几乎立即能得到正确的响应当然这个响应其实并不是立即发生的它毕竟和那种由中断驱动的系统不同只有线程拥有CPU的执行时间并注意到标记已发生改变计数器才会停止 用内部类改善代码 下面说说题外话请大家注意一下SeparateSubTask和Counter类之间发生的结合行为SeparateSubTask同Counter亲密地结合到了一起——它必须持有指向自己父Counter对象的一个句柄以便自己能回调和操纵它但两个类并不是真的合并为单独一个类(尽管在下一节中我们会讲到Java确实提供了合并它们的方法)因为它们各自做的是不同的事情而且是在不同的时间创建的但不管怎样它们依然紧密地结合到一起(更准确地说应该叫联合)所以使程序代码多少显得有些笨拙在这种情况下一个内部类可以显着改善代码的可读性和执行效率 //: Counterijava // Counter using an inner class for the thread import javaawt*; import javaawtevent*; import javaapplet*; public class Counteri extends Applet { private class SeparateSubTask extends Thread { int count = ; boolean runFlag = true; SeparateSubTask() { start(); } public void run() { while (true) { try { sleep(); } catch (InterruptedException e){} if(runFlag) tsetText(IntegertoString(count++)); } } } private SeparateSubTask sp = null; private TextField t = new TextField(); private Button onOff = new Button(Toggle) start = new Button(Start); public void init() { add(t); startaddActionListener(new StartL()); add(start); onOffaddActionListener(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) sprunFlag = !sprunFlag; // invertFlag(); } } public static void main(String[] args) { Counteri applet = new Counteri(); Frame aFrame = new Frame(Counteri); aFrameaddWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { Systemexit(); } }); aFrameadd(applet BorderLayoutCENTER); aFramesetSize(); appletinit(); appletstart(); aFramesetVisible(true); } } 这个SeparateSubTask名字不会与前例中的SeparateSubTask沖突——即使它们都在相同的目录里——因为它已作为一个内部类隐藏起来大家亦可看到内部类被设为private(私有)属性这意味着它的字段和方法都可获得默认的访问权限(run()除外它必须设为public因为它在基础类中是公开的)除Counteri之外其他任何方面都不可访问private内部类而且由于两个类紧密结合在一起所以很容易放宽它们之间的访问限制在SeparateSubTask中我们可看到invertFlag()方法已被删去因为Counteri现在可以直接访问runFlag 此外注意SeparateSubTask的构建器已得到了简化——它现在唯一的用外就是启动线程Counteri对象的句柄仍象以前那样得以捕获但不再是通过人工传递和引用外部对象来达到这一目的此时的内部类机制可以自动照料它在run()中可看到对t的访问是直接进行的似乎它是SeparateSubTask的一个字段父类中的t字段现在可以变成private因为SeparateSubTask能在未获任何特殊许可的前提下自由地访问它——而且无论如何都该尽可能地把字段变成私有属性以防来自类外的某种力量不慎地改变它们 无论在什么时候只要注意到类相互之间结合得比较紧密就可考虑利用内部类来改善代码的编写与维护 |