八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第129部分

Java编程思想第4版[中文版](PDF格式)-第129部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




    applet。init();  

    applet。start();  

    aFrame。setVisible(true);  

  }  

} ///:~  

  

Ticker 不仅包括了自己的线程处理机制,也提供了控制与显示线程的工具。可按自己的意愿创建任意数量的 

线程,毋需明确地创建窗口化组件。  

在Counter4 中,有一个名为s 的Ticker 对象的数组。为获得最大的灵活性,这个数组的长度是用程序片参 

数接触Web 页而初始化的。下面是网页中长度参数大致的样子,它们嵌于对程序片(applet)的描述内容 

中:  

  

  

  

其中,param,name 和 value 是所有 Web 页都适用的关键字。name 是指程序中对参数的一种引用称谓,value 

可以是任何字串(并不仅仅是解析成一个数字的东西)。  

我们注意到对数组 s 长度的判断是在 init()内部完成的,它没有作为s 的内嵌定义的一部分提供。换言之, 

不可将下述代码作为类定义的一部分使用(应该位于任何方法的外部):  

inst size = Integer。parseInt(getParameter(〃Size〃));  

Ticker'' s = new Ticker'size'  

可把它编译出来,但会在运行期得到一个空指针违例。但若将 getParameter()初始化移入 init(),则可正常 

工作。程序片框架会进行必要的启动工作,以便在进入 init()前收集好一些参数。  

此外,上述代码被同时设置成一个程序片和一个应用(程序)。在它是应用程序的情况下,size 参数可从命 

令行里提取出来(否则就提供一个默认的值)。  

数组的长度建好以后,就可以创建新的Ticker 对象;作为 Ticker 构建器的一部分,用于每个Ticker 的按钮 

和文本字段就会加入程序片。  

按下Start 按钮后,会在整个 Ticker 数组里遍历,并为每个 Ticker 调用 start()。记住,start()会进行必 

要的线程初始化工作,然后为那个线程调用run()。  

ToggleL 监视器只是简单地切换 Ticker 中的标记,一旦对应线程以后需要修改这个标记,它会作出相应的反 

应。  

这个例子的一个好处是它使我们能够方便地创建由单独子任务构成的大型集合,并以监视它们的行为。在这 

种情况下,我们会发现随着子任务数量的增多,机器显示出来的数字可能会出现更大的分歧,这是由于为线 

程提供服务的方式造成的。  



                                                                                       497 


…………………………………………………………Page 499……………………………………………………………

亦可试着体验一下 sleep(100)在 Ticker。run()中的重要作用。若删除 sleep(),那么在按下一个切换按钮 

前,情况仍然会进展良好。按下按钮以后,那个特定的线程就会出现一个失败的 runFlag,而且 run()会深深 

地陷入一个无限循环——很难在多任务处理期间中止退出。因此,程序对用户操作的反应灵敏度会大幅度降 

低。  



14。1。5 Daemon 线程  



 “Daemon”线程的作用是在程序的运行期间于后台提供一种“常规”服务,但它并不属于程序的一个基本部 

分。因此,一旦所有非 Daemon 线程完成,程序也会中止运行。相反,假若有任何非 Daemon 线程仍在运行 

 (比如还有一个正在运行main()的线程),则程序的运行不会中止。  

通过调用 isDaemon(),可调查一个线程是不是一个Daemon,而且能用 setDaemon()打开或者关闭一个线程的 

Daemon 状态。如果是一个 Daemon 线程,那么它创建的任何线程也会自动具备Daemon 属性。  

下面这个例子演示了Daemon 线程的用法:  

  

//: Daemons。java  

// Daemonic behavior  

import java。io。*;  

  

class Daemon extends Thread {  

  private static final int SIZE = 10;  

  private Thread'' t = new Thread'SIZE';  

  public Daemon() {   

    setDaemon(true);  

    start();  

  }  

  public void run() {  

    for(int i = 0; i 《 SIZE; i++)  

      t'i' = new DaemonSpawn(i);  

    for(int i = 0; i 《 SIZE; i++)  

      System。out。println(  

        〃t'〃 + i + 〃'。isDaemon() = 〃   

        + t'i'。isDaemon());  

    while(true)   

      yield();  

  }  

}  

  

class DaemonSpawn extends Thread {  

  public DaemonSpawn(int i) {  

    System。out。println(  

      〃DaemonSpawn 〃 + i + 〃 started〃);  

    start();  

  }  

  public void run() {  

    while(true)   

      yield();  

  }  

}  

  

public class Daemons {  

  public static void main(String'' args) {  

    Thread d = new Daemon();  

    System。out。println(  



                                                                                             498 


…………………………………………………………Page 500……………………………………………………………

      〃d。isDaemon() = 〃 + d。isDaemon());  

    // Allow the daemon threads to finish  

    // their startup processes:  

    BufferedReader stdin =  

      new BufferedReader(  

        new InputStreamReader(System。in));  

    System。out。println(〃Waiting for CR〃);  

    try {  

      stdin。readLine();  

    } catch(IOException e) {}  

  }  

} ///:~  

  

Daemon 线程可将自己的Daemon 标记设置成“真”,然后产生一系列其他线程,而且认为它们也具有 Daemon 

属性。随后,它进入一个无限循环,在其中调用yield(),放弃对其他进程的控制。在这个程序早期的一个 

版本中,无限循环会使 int计数器增值,但会使整个程序都好象陷入停顿状态。换用 yield()后,却可使程 

序充满“活力”,不会使人产生停滞或反应迟钝的感觉。  

一旦main()完成自己的工作,便没有什么能阻止程序中断运行,因为这里运行的只有 Daemon 线程。所以能 

看到启动所有Daemon 线程后显示出来的结果,System。in 也进行了相应的设置,使程序中断前能等待一个回 

车。如果不进行这样的设置,就只能看到创建 Daemon 线程的一部分结果(试试将readLine()代码换成不同 

长度的 sleep()调用,看看会有什么表现)。  



14。2 共享有限的资源  



可将单线程程序想象成一种孤立的实体,它能遍历我们的问题空间,而且一次只能做一件事情。由于只有一 

个实体,所以永远不必担心会有两个实体同时试图使用相同的资源,就象两个人同时都想停到一个车位,同 

时都想通过一扇门,甚至同时发话。  

进入多线程环境后,它们则再也不是孤立的。可能会有两个甚至更多的线程试图同时同一个有限的资源。必 

须对这种潜在资源冲突进行预防,否则就可能发生两个线程同时访问一个银行帐号,打印到同一台计算机, 

以及对同一个值进行调整等等。  



14。2。1  资源访问的错误方法  



现在考虑换成另一种方式来使用本章频繁见到的计数器。在下面的例子中,每个线程都包含了两个计数器, 

它们在 run()里增值以及显示。除此以外,我们使用了 Watcher 类的另一个线程。它的作用是监视计数器, 

检查它们是否保持相等。这表面是一项无意义的行动,因为如果查看代码,就会发现计数器肯定是相同的。 

但实际情况却不一定如此。下面是程序的第一个版本:  

  

//: Sharing1。java  

// Problems with resource sharing while threading  

import java。awt。*;  

import java。awt。event。*;  

import java。applet。*;  

  

class TwoCounter extends Thread {  

  private boolean started = false;  

  private TextField   

    t1 = new TextField(5);  

    t2 = new TextField(5);  

  private Label l =   

    new Label(〃count1 == count2〃);  

  private int count1 = 0; count2 = 0;  

  // Add the display ponents as a panel  



                                                                                     499 


…………………………………………………………Page 501……………………………………………………………

  // to the given container:  

  public TwoCounter(Container c) {  

    Panel p = new Panel();  

    p。add(t1);  

    p。add(t2);  

    p。add(l);  

    c。add(p);  

  }  

  public void start() {  

    if(!started) {  

      started = true;  

      super。start();  

    }  

  }  

  public void run() {  

    while (true) {  

      t1。setText(Integer。toString(count1++));  

      t2。setText(Integer。toString(count2++));  

      try {  

        sleep(500);  

      } catch (InterruptedException e){}  

    }  

  }  

  public void synchTest() {  

    Sharing1。incrementAccess();  

    if(count1 != count2)  

      l。setText(〃Unsynched〃);  

  }  

}  

  

class Watcher extends Thread {  

  private Sharing1 p;  

  public Watcher(Sharing1 p) {   

    this。p = p;  

    start();  

  }  

  public void run() {  

    while(true) {  

      for(int i = 0; i 《 p。s。length; i++)  

        p。s'i'。synchTest();  

      try {  

        sleep(500);  

      } catch (InterruptedException e){}  

    }  

  }  

}  

  

public class Sharing1 extends Applet {  

  TwoCounter'' s;  

  private static int accessCount = 0;  

  private static TextField aCount =   

    new TextField(〃0〃; 10);  



                                                                                             500 


…………………………………………………………Page 502……………………………………………………………

  public static void incrementAccess() {  

    accessCount++;  

    aCount。setText(Integer。toString(accessCount));  

  }  

  private Button   

    start = new Button(〃Start〃);  

    observer = new Button(〃Observe〃);  

  private boolean isApplet = true;  

  private int numCounters = 0;  

  private int numObservers = 0;  

  public void init() {  

    if(isApplet) {  

      numCounters =   

        Integer。parseInt(getParameter(〃size〃));  

      numObservers =   

        Integer。parseInt(  

          getParameter(〃observers〃));  

    }  

    s = new TwoCounter'numCounters';  

    for (int i = 0; i 《 s。length; i++)  

      s'i' = new TwoCounter(this);  

    Panel p = new Panel();  

    start。addActionListener(new StartL());  

    p。add(start);  

    observer。addActionListener(new ObserverL());  

    p。add(observer);  

    p。add(new Label(〃Access Count〃));  

    p。add(aCount);  

    add(p);  

  }  

  class StartL implements ActionListener {  

    public void actionPerformed(ActionEvent e) {  

      for(int i = 0; i 《 s。length; i++)  

        s'i'。start();  

    }  

  }  

  class ObserverL implements ActionListener {  

    public void actionPerformed(ActionEvent e) {  

      for(int i = 0; i 《 numObservers; i++)  

        new Watcher(Sharing1。this);  

    }  

  }  

  public static void main(String'' args) {  

    Sharing1 applet = new Sharing1();  

    // This isn't an applet; so set the flag and  

    // produce the parameter values from args:  

    applet。isApplet = false;  

    applet。numCounters =   

      (args。length == 0 ? 5 :  

        Integer。parseInt(args'0'));  

    applet。numObservers =  

      (args。length 《 2 ? 5 :  



                                                                                        501 


…………………………………………………………Page 503……………………………………………………………

        Integer。parseInt(args'1'));  

    Frame aFrame = new Frame(〃Sharing1〃);  

    aFrame。addWindowListener(  

      new WindowAdapter() {  


返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的