?? 0120.htm
字號:
在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對象,用它們填充網格,并為每一
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -