14.1.3
用主类合并线程
在上面的例子中,我们看到线程类(
Thread
)与程序的主类(
Main
)是分隔
开的。这样做非常合理,而且易于理解。然而,还有另一种方式也是经常要用到
的。尽管它不十分明确,但一般都要更简洁一些(这也解释了它为什么十分流行)。
通过将主程序类变成一个线程,这种形式可将主程序类与线程类合并到一起。由
于对一个
GUI
程序来说,主程序类必须从
Frame
或
Applet
继承,所以必须用一
个接口加入额外的功能。这个接口叫作
Runnable
,其中包含了与
Thread
一致的
基本方法。事实上,
Thread
也实现了
Runnable
,它只指出有一个
run()
方法。
对合并后的程序/线程来说,它的用法不是十分明确。当我们启动程序时,
会创建一个
Runnable
(可运行的)对象,但不会自行启动线程。线程的启动必须
明确进行。下面这个程序向我们演示了这一点,它再现了
Counter2
的功能:
762-763
页程序
1
现在
run()
位于类内,但它在
init()
结束以后仍处在“睡眠”状态。若按下启
动按钮,线程便会用多少有些暧昧的表达方式创建(若线程尚不存在):
new Thread(Counter3.this);
若某样东西有一个
Runnable
接口,实际只是意味着它有一个
run()
方法,但
不存在与之相关的任何特殊东西——它不具有任何天生的线程处理能力,这与那
些从
Thread
继承的类是不同的。所以为了从一个
Runnable
对象产生线程,必须
单独创建一个线程,并为其传递
Runnable
对象;可为其使用一个特殊的构建器,
并令其采用一个
Runnable
作为自己的参数使用。随后便可为那个线程调用
start()
,
如下所示:
selfThread.start();
它的作用是执行常规初始化操作,然后调用
run()
。
Runnable
接口最大的一个优点是所有东西都从属于相同的类。若需访问什么
东西,只需简单地访问它即可,不需要涉及一个独立的对象。但为这种便利也是
要付出代价的——只可为那个特定的对象运行单独一个线程(尽管可创建那种类
型的多个对象,或者在不同的类里创建其他对象)。
注意
Runnable
接口本身并不是造成这一限制的罪魁祸首。它是由于
Runnable
与我们的主类合并造成的,因为每个应用只能主类的一个对象。
14.1.4
制作多个线程
现在考虑一下创建多个不同的线程的问题。我们不可用前面的例子来做到这
一点,所以必须倒退回去,利用从
Thread
继承的多个独立类来封装
run()
。但这
是一种更常规的方案,而且更易理解,所以尽管前例揭示了我们经常都能看到的
编码样式,但并不推荐在大多数情况下都那样做,因为它只是稍微复杂一些,而
且灵活性稍低一些。
下面这个例子用计数器和切换按钮再现了前面的编码样式。但这一次,一个
特定计数器的所有信息(按钮和文本字段)都位于它自己的、从
Thread
继承的
对象内。
Ticker
中的所有字段都具有
private
(私有)属性,这意味着
Ticker
的具
体实现方案可根据实际情况任意修改,其中包括修改用于获取和显示信息的数据
组件的数量及类型。创建好一个
Ticker
对象以后,构建器便请求一个
AWT
容器
(
Container
)的句柄——
Ticker
用自己的可视组件填充那个容器。采用这种方式,