返回介绍

14.1.3 用主类合并线程

发布于 2024-10-15 23:56:31 字数 2660 浏览 0 评论 0 收藏 0

在上面的例子中,我们看到线程类(Thread)与程序的主类(Main)是分隔开的。这样做非常合理,而且易于理解。然而,还有另一种方式也是经常要用到的。尽管它不十分明确,但一般都要更简洁一些(这也解释了它为什么十分流行)。通过将主程序类变成一个线程,这种形式可将主程序类与线程类合并到一起。由于对一个 GUI 程序来说,主程序类必须从 Frame 或 Applet 继承,所以必须用一个接口加入额外的功能。这个接口叫作 Runnable,其中包含了与 Thread 一致的基本方法。事实上,Thread 也实现了 Runnable,它只指出有一个 run() 方法。

对合并后的程序/线程来说,它的用法不是十分明确。当我们启动程序时,会创建一个 Runnable(可运行的)对象,但不会自行启动线程。线程的启动必须明确进行。下面这个程序向我们演示了这一点,它再现了 Counter2 的功能:

//: Counter3.java
// Using the Runnable interface to turn the 
// main class into a thread.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Counter3 
    extends Applet implements Runnable {
  private int count = 0;
  private boolean runFlag = true;
  private Thread selfThread = null;
  private Button 
    onOff = new Button("Toggle"),
    start = new Button("Start");
  private TextField t = new TextField(10);
  public void init() {
    add(t);
    start.addActionListener(new StartL());
    add(start);
    onOff.addActionListener(new OnOffL());
    add(onOff);
  }
  public void run() {
    while (true) {
      try {
        selfThread.sleep(100);
      } catch (InterruptedException e){}
      if(runFlag) 
        t.setText(Integer.toString(count++));
    }
  }
  class StartL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if(selfThread == null) {
        selfThread = new Thread(Counter3.this);
        selfThread.start();
      }
    }
  }
  class OnOffL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      runFlag = !runFlag;
    }
  }
  public static void main(String[] args) {
    Counter3 applet = new Counter3();
    Frame aFrame = new Frame("Counter3");
    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);
  }
} ///:~

现在 run() 位于类内,但它在 init() 结束以后仍处在“睡眠”状态。若按下启动按钮,线程便会用多少有些暧昧的表达方式创建(若线程尚不存在):

new Thread(Counter3.this);

若某样东西有一个 Runnable 接口,实际只是意味着它有一个 run() 方法,但不存在与之相关的任何特殊东西——它不具有任何天生的线程处理能力,这与那些从 Thread 继承的类是不同的。所以为了从一个 Runnable 对象产生线程,必须单独创建一个线程,并为其传递 Runnable 对象;可为其使用一个特殊的构建器,并令其采用一个 Runnable 作为自己的参数使用。随后便可为那个线程调用 start(),如下所示:

selfThread.start();

它的作用是执行常规初始化操作,然后调用 run()。

Runnable 接口最大的一个优点是所有东西都从属于相同的类。若需访问什么东西,只需简单地访问它即可,不需要涉及一个独立的对象。但为这种便利也是要付出代价的——只可为那个特定的对象运行单独一个线程(尽管可创建那种类型的多个对象,或者在不同的类里创建其他对象)。

注意 Runnable 接口本身并不是造成这一限制的罪魁祸首。它是由于 Runnable 与我们的主类合并造成的,因为每个应用只能主类的一个对象。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文