Java编程思想第4版[中文版](PDF格式)-第135部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
…………………………………………………………Page 525……………………………………………………………
class UpMaxL implements ActionListener {
public void actionPerformed(ActionEvent e) {
int maxp =
s'0'。getThreadGroup()。getMaxPriority();
if(++maxp 》 Thread。MAX_PRIORITY)
maxp = Thread。MAX_PRIORITY;
s'0'。getThreadGroup()。setMaxPriority(maxp);
showMaxPriority();
}
}
class DownMaxL implements ActionListener {
public void actionPerformed(ActionEvent e) {
int maxp =
s'0'。getThreadGroup()。getMaxPriority();
if(……maxp 《 Thread。MIN_PRIORITY)
maxp = Thread。MIN_PRIORITY;
s'0'。getThreadGroup()。setMaxPriority(maxp);
showMaxPriority();
}
}
public static void main(String'' args) {
Counter5 applet = new Counter5();
Frame aFrame = new Frame(〃Counter5〃);
aFrame。addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System。exit(0);
}
});
aFrame。add(applet; BorderLayout。CENTER);
aFrame。setSize(300; 600);
applet。init();
applet。start();
aFrame。setVisible(true);
}
} ///:~
Ticker 采用本章前面构造好的形式,但有一个额外的 TextField (文本字段),用于显示线程的优先级;以
及两个额外的按钮,用于人为提高及降低优先级。
也要注意yield()的用法,它将控制权自动返回给调试程序(机制)。若不进行这样的处理,多线程机制仍
会工作,但我们会发现它的运行速度慢了下来(试试删去对yield()的调用)。亦可调用sleep(),但假若那
样做,计数频率就会改由 sleep()的持续时间控制,而不是优先级。
Counter5 中的init()创建了由 10个 Ticker2 构成的一个数组;它们的按钮以及输入字段(文本字段)由
Ticker2 构建器置入窗体。Counter5 增加了新的按钮,用于启动一切,以及用于提高和降低线程组的最大优
先级。除此以外,还有一些标签用于显示一个线程可以采用的最大及最小优先级;以及一个特殊的文本字
段,用于显示线程组的最大优先级(在下一节里,我们将全面讨论线程组的问题)。最后,父线程组的优先
级也作为标签显示出来。
按下“up”(上)或“down”(下)按钮的时候,会先取得Ticker2 当前的优先级,然后相应地提高或者降
低。
运行该程序时,我们可注意到几件事情。首先,线程组的默认优先级是5。即使在启动线程之前(或者在创
建线程之前,这要求对代码进行适当的修改)将最大优先级降到5 以下,每个线程都会有一个5 的默认优先
级。
524
…………………………………………………………Page 526……………………………………………………………
最简单的测试是获取一个计数器,将它的优先级降低至 1,此时应观察到它的计数频率显著放慢。现在试着
再次提高优先级,可以升高回线程组的优先级,但不能再高了。现在将线程组的优先级降低两次。线程的优
先级不会改变,但假若试图提高或者降低它,就会发现这个优先级自动变成线程组的优先级。此外,新线程
仍然具有一个默认优先级,即使它比组的优先级还要高(换句话说,不要指望利用组优先级来防止新线程拥
有比现有的更高的优先级)。
最后,试着提高组的最大优先级。可以发现,这样做是没有效果的。我们只能减少线程组的最大优先级,而
不能增大它。
14。4。1 线程组
所有线程都隶属于一个线程组。那可以是一个默认线程组,亦可是一个创建线程时明确指定的组。在创建之
初,线程被限制到一个组里,而且不能改变到一个不同的组。每个应用都至少有一个线程从属于系统线程
组。若创建多个线程而不指定一个组,它们就会自动归属于系统线程组。
线程组也必须从属于其他线程组。必须在构建器里指定新线程组从属于哪个线程组。若在创建一个线程组的
时候没有指定它的归属,则同样会自动成为系统线程组的一名属下。因此,一个应用程序中的所有线程组最
终都会将系统线程组作为自己的“父”。
之所以要提出“线程组”的概念,很难从字面上找到原因。这多少为我们讨论的主题带来了一些混乱。一般
地说,我们认为是由于“安全”或者“保密”方面的理由才使用线程组的。根据 Arnold 和 Gosling 的说法:
“线程组中的线程可以修改组内的其他线程,包括那些位于分层结构最深处的。一个线程不能修改位于自己
所在组或者下属组之外的任何线程”(注释①)。然而,我们很难判断“修改”在这儿的具体含义是什么。
下面这个例子展示了位于一个“叶子组”内的线程能修改它所在线程组树的所有线程的优先级,同时还能为
这个“树”内的所有线程都调用一个方法。
①:《The Java Programming Language》第 179 页。该书由 Arnold 和 Jams Gosling 编著,Addison…Wesley
于 1996 年出版
//: TestAccess。java
// How threads can access other threads
// in a parent thread group
public class TestAccess {
public static void main(String'' args) {
ThreadGroup
x = new ThreadGroup(〃x〃);
y = new ThreadGroup(x; 〃y〃);
z = new ThreadGroup(y; 〃z〃);
Thread
one = new TestThread1(x; 〃one〃);
two = new TestThread2(z; 〃two〃);
}
}
class TestThread1 extends Thread {
private int i;
TestThread1(ThreadGroup g; String name) {
super(g; name);
}
void f() {
i++; // modify this thread
System。out。println(getName() + 〃 f()〃);
}
}
525
…………………………………………………………Page 527……………………………………………………………
class TestThread2 extends TestThread1 {
TestThread2(ThreadGroup g; String name) {
super(g; name);
start();
}
public void run() {
ThreadGroup g =
getThreadGroup()。getParent()。getParent();
g。list();
Thread'' gAll = new Thread'g。act iveCount()';
g。enumerate(gAll);
for(int i = 0; i 《 gAll。length; i++) {
gAll'i'。setPriority(Thread。MIN_PRIORITY);
((TestThread1)gAll'i')。f();
}
g。list();
}
} ///:~
在main()中,我们创建了几个ThreadGroup (线程组),每个都位于不同的“叶”上:x 没有参数,只有它
的名字(一个String),所以会自动进入“system”(系统)线程组;y 位于x 下方,而 z位于y 下方。注
意初始化是按照文字顺序进行的,所以代码合法。
有两个线程创建之后进入了不同的线程组。其中,TestThread1 没有一个 run()方法,但有一个f(),用于通
知线程以及打印出一些东西,以便我们知道它已被调用。而TestThread2 属于TestThread1 的一个子类,它
的run()非常详尽,要做许多事情。首先,它获得当前线程所在的线程组,然后利用getParent()在继承树中
向上移动两级(这样做是有道理的,因为我想把TestThread2 在分级结构中向下移动两级)。随后,我们调
用方法 activeCount() ,查询这个线程组以及所有子线程组内有多少个线程,从而创建由指向Thread 的句柄
构成的一个数组。enumerate()方法将指向所有这些线程的句柄置入数组 gAll 里。然后在整个数组里遍历,
为每个线程都调用 f()方法,同时修改优先级。这样一来,位于一个“叶子”线程组里的线程就修改了位于
父线程组的线程。
调试方法 list()打印出与一个线程组有关的所有信息,把它们作为标准输出。在我们对线程组的行为进行调
查的时候,这样做是相当有好处的。下面是程序的输出:
java。lang。ThreadGroup'name=x;maxpri=10'
Thread'one;5;x'
java。lang。ThreadGroup'name=y;maxpri=10'
java。lang。ThreadGroup'name=z;maxpri=10'
Thread'two;5;z'
one f()
two f()
java。lang。ThreadGroup'name=x;maxpri=10'
Thread'one;1;x'
java。lang。ThreadGroup'name=y;maxpri=10'
java。lang。ThreadGroup'name=z;maxpri=10'
Thread'two;1;z'
list()不仅打印出 ThreadGroup 或者Thread 的类名,也打印出了线程组的名字以及它的最高优先级。对于线
程,则打印出它们的名字,并接上线程优先级以及所属的线程组。注意 list()会对线程和线程组进行缩排处
理,指出它们是未缩排的线程组的“子”。
大家可看到 f()是由TestThread2 的run()方法调用的,所以很明显,组内的所有线程都是相当脆弱的。然
而,我们只能访问那些从自己的system 线程组树分支出来的线程,而且或许这就是所谓“安全”的意思。我
们不能访问其他任何人的系统线程树。
526
…………………………………………………………Page 528……………………………………………………………
1。 线程组的控制
抛开安全问题不谈,线程组最有用的一个地方就是控制:只需用单个命令即可完成对整个线程组的操作。下
面这个例子演示了这一点,并对线程组内优先级的限制进行了说明。括号内的注释数字便于大家比较输出结
果:
//: ThreadGroup1。java
// How thread groups control priorities
// of the threads inside them。
public class ThreadGroup1 {
public static void main(String'' args) {
// Get the system thread & print its Info:
ThreadGroup sys =
Thread。currentThread()。getThreadGroup();
sys。list(); // (1)
// Reduce the system thread group priority:
sys。setMaxPriority(Thread。MAX_PRIORITY 1);
// Increase the main thread priority:
Thread curr = Thread。currentThread();
curr。setPriority(curr。getPriority() + 1);
sys。list(); // (2)
// Attempt to set a new group to the max:
ThreadGroup g1 = new ThreadGroup(〃g1〃);
g1。setMaxPriority(Thread。MAX_PRIORITY);
// Attempt to set a new thread to the max:
Thread t = new Thread(g1; 〃A〃);
t。setPriority(Thread。MAX_PRIORITY);
g1。list(); // (3)
// Reduce g1's max priority; then attempt
// to increase it:
g1。setMaxPriority(Thread。MAX_PRIORITY 2);
g1。setMaxPriority(Thread。MAX_PRIORITY);
g1。list(); // (4)
// Attempt to set a new thread to the max:
t = new Thread(g1; 〃B〃);
t。setPriority(Thread。MAX_PRIORITY);
g1。list(); // (5)
// Lower the max priority below the default
// thread priority:
g1。setMaxPriority(Thread。MIN_PRIORITY + 2);
// Look at a new thread's priority before
// and after changing it:
t = new Thread(g1; 〃C〃);
g1。list(); // (6)
t。setPriority(t。getPriority() …1);
g1。list(); // (7)
// Make g2 a child Threadgroup of g1 and
// try to increase its priority:
ThreadGroup g2 = new ThreadGroup(g1; 〃g2〃);
g2。list(); // (8)
g2。setMaxPriority(Thread。MAX_PRIORITY);
527
…………………………………………………………Page 529……………………………………………………………
g2。list(); // (9)