⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter14.htm

📁 一本Java由初级到高级的编程书籍
💻 HTM
📖 第 1 页 / 共 5 页
字号:
在main()中,我们创建了几个ThreadGroup(线程组),每个都位于不同的“叶”上:x没有参数,只有它的名字(一个String),所以会自动进入“system”(系统)线程组;y位于x下方,而z位于y下方。注意初始化是按照文字顺序进行的,所以代码合法。<br>
有两个线程创建之后进入了不同的线程组。其中,TestThread1没有一个run()方法,但有一个f(),用于通知线程以及打印出一些东西,以便我们知道它已被调用。而TestThread2属于TestThread1的一个子类,它的run()非常详尽,要做许多事情。首先,它获得当前线程所在的线程组,然后利用getParent()在继承树中向上移动两级(这样做是有道理的,因为我想把TestThread2在分级结构中向下移动两级)。随后,我们调用方法activeCount(),查询这个线程组以及所有子线程组内有多少个线程,从而创建由指向Thread的句柄构成的一个数组。enumerate()方法将指向所有这些线程的句柄置入数组gAll里。然后在整个数组里遍历,为每个线程都调用f()方法,同时修改优先级。这样一来,位于一个“叶子”线程组里的线程就修改了位于父线程组的线程。<br>
调试方法list()打印出与一个线程组有关的所有信息,把它们作为标准输出。在我们对线程组的行为进行调查的时候,这样做是相当有好处的。下面是程序的输出:<br>
<br>
808页下程序<br>
<br>
list()不仅打印出ThreadGroup或者Thread的类名,也打印出了线程组的名字以及它的最高优先级。对于线程,则打印出它们的名字,并接上线程优先级以及所属的线程组。注意list()会对线程和线程组进行缩排处理,指出它们是未缩排的线程组的“子”。<br>
大家可看到f()是由TestThread2的run()方法调用的,所以很明显,组内的所有线程都是相当脆弱的。然而,我们只能访问那些从自己的system线程组树分支出来的线程,而且或许这就是所谓“安全”的意思。我们不能访问其他任何人的系统线程树。<br>
<br>
1. 线程组的控制<br>
抛开安全问题不谈,线程组最有用的一个地方就是控制:只需用单个命令即可完成对整个线程组的操作。下面这个例子演示了这一点,并对线程组内优先级的限制进行了说明。括号内的注释数字便于大家比较输出结果:<br>
<br>
809-810页程序<br>
<br>
下面的输出结果已进行了适当的编辑,以便用一页能够装下(java.lang.已被删去),而且添加了适当的数字,与前面程序列表中括号里的数字对应:<br>
<br>
811页程序<br>
<br>
所有程序都至少有一个线程在运行,而且main()采取的第一项行动便是调用Thread的一个static(静态)方法,名为currentThread()。从这个线程开始,线程组将被创建,而且会为结果调用list()。输出如下:<br>
<br>
812页上程序<br>
<br>
我们可以看到,主线程组的名字是system,而主线程的名字是main,而且它从属于system线程组。<br>
第二个练习显示出system组的最高优先级可以减少,而且main线程可以增大自己的优先级:<br>
<br>
812页中上程序<br>
<br>
第三个练习创建一个新的线程组,名为g1;它自动从属于system线程组,因为并没有明确指定它的归属关系。我们在g1内部放置了一个新线程,名为A。随后,我们试着将这个组的最大优先级设到最高的级别,并将A的优先级也设到最高一级。结果如下:<br>
<br>
812页中程序<br>
<br>
可以看出,不可能将线程组的最大优先级设为高于它的父线程组。<br>
第四个练习将g1的最大优先级降低两级,然后试着把它升至Thread.MAX_PRIORITY。结果如下:<br>
<br>
812页中下程序<br>
<br>
同样可以看出,提高最大优先级的企图是失败的。我们只能降低一个线程组的最大优先级,而不能提高它。此外,注意线程A的优先级并未改变,而且它现在高于线程组的最大优先级。也就是说,线程组最大优先级的变化并不能对现有线程造成影响。<br>
第五个练习试着将一个新线程设为最大优先级。如下所示:<br>
<br>
812页下程序<br>
<br>
因此,新线程不能变到比最大线程组优先级还要高的一级。<br>
这个程序的默认线程优先级是6;若新建一个线程,那就是它的默认优先级,而且不会发生变化,除非对优先级进行了特别的处理。练习六将把线程组的最大优先级降至默认线程优先级以下,看看在这种情况下新建一个线程会发生什么事情:<br>
<br>
813页上程序<br>
<br>
尽管线程组现在的最大优先级是3,但仍然用默认优先级6来创建新线程。所以,线程组的最大优先级不会影响默认优先级(事实上,似乎没有办法可以设置新线程的默认优先级)。<br>
改变了优先级后,接下来试试将其降低一级,结果如下:<br>
<br>
813页中程序<br>
<br>
因此,只有在试图改变优先级的时候,才会强迫遵守线程组最大优先级的限制。<br>
我们在(8)和(9)中进行了类似的试验。在这里,我们创建了一个新的线程组,名为g2,将其作为g1的一个子组,并改变了它的最大优先级。大家可以看到,g2的优先级无论如何都不可能高于g1:<br>
<br>
813页中下程序<br>
<br>
也要注意在g2创建的时候,它会被自动设为g1的线程组最大优先级。<br>
经过所有这些实验以后,整个线程组和线程系统都会被打印出来,如下所示:<br>
<br>
813-814页程序<br>
<br>
所以由线程组的规则所限,一个子组的最大优先级在任何时候都只能低于或等于它的父组的最大优先级。<br>
本程序的最后一个部分演示了用于整组线程的方法。程序首先遍历整个线程树,并启动每一个尚未启动的线程。例如,system组随后会被挂起(暂停),最后被中止(尽管用suspend()和stop()对整个线程组进行操作看起来似乎很有趣,但应注意这些方法在Java 
1.2里都是被“反对”的)。但在挂起system组的同时,也挂起了main线程,而且整个程序都会关闭。所以永远不会达到让线程中止的那一步。实际上,假如真的中止了main线程,它会“掷”出一个ThreadDeath违例,所以我们通常不这样做。由于ThreadGroup是从Object继承的,其中包含了wait()方法,所以也能调用wait(秒数×1000),令程序暂停运行任意秒数的时间。当然,事前必须在一个同步块里取得对象锁。<br>
ThreadGroup类也提供了suspend()和resume()方法,所以能中止和启动整个线程组和它的所有线程,也能中止和启动它的子组,所有这些只需一个命令即可(再次提醒,suspend()和resume()都是Java 
1.2所“反对”的)。<br>
从表面看,线程组似乎有些让人摸不着头脑,但请注意我们很少需要直接使用它们。<br>
<br>
14.5 回顾runnable<br>
在本章早些时候,我曾建议大家在将一个程序片或主Frame当作Runnable的实现形式之前,一定要好好地想一想。若采用那种方式,就只能在自己的程序中使用其中的一个线程。这便限制了灵活性,一旦需要用到属于那种类型的多个线程,就会遇到不必要的麻烦。<br>
当然,如果必须从一个类继承,而且想使类具有线程处理能力,则Runnable是一种正确的方案。本章最后一个例子对这一点进行了剖析,制作了一个RunnableCanvas类,用于为自己描绘不同的颜色(Canvas是“画布”的意思)。这个应用被设计成从命令行获得参数值,以决定颜色网格有多大,以及颜色发生变化之间的sleep()有多长。通过运用这些值,大家能体验到线程一些有趣而且可能令人费解的特性:<br>
<br>
815-816页程序<br>
<br>
ColorBoxes是一个典型的应用(程序),有一个构建器用于设置GUI。这个构建器采用int 
grid的一个参数,用它设置GridLayout(网格布局),使每一维里都有一个grid单元。随后,它添加适当数量的CBox对象,用它们填充网格,并为每一个都传递pause值。在main()中,我们可看到如何对pause和grid的默认值进行修改(如果用命令行参数传递)。<br

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -