多線程編程——基礎篇 (三)

  線程對象的幾個重要的方法

  儘管線程對象的常用方法可以通過API文檔來了解,但是有很多方法僅僅從API說明是無法詳細瞭解的。

  本來打算用一節的篇幅來把線程方法中一些重要的知識說完,但這樣下來估計要很常的篇幅,可能要用好幾節才能說把和線程方法相關的一些重要的知識說完。

  首先我們接基礎篇(二)來說明start()方法。

  一個線程對象生成後,如果要產生一個執行的線程,就一定要調用它的start()方法.在介紹這個方法時不得不同時說明run方法.其實線程對象的run方法完全是一個接口回調方法,它是你這個線程對象要完成的具體邏輯.簡單說你要做什麼就你在run中完成,而如何做,什麼時候做就不需要你控制了,你只要調用start()方法,JVM就會管理這個線程對象讓它產生一個線程並註冊到線程處理系統中。

  從表面上看,start()方法調用了run()方法,事實上,start()方法並沒有直接調用run方法.在JDK1.5以前start()方法是本地方法,它如何最終調用run方法已經不是JAVA程序員所能瞭解的.而在JDK1.5中,原來的那個本地start()方法被start0()代替,另個一個純JAVA的start()中調用本地方法start0(),而在start()方法中做了一個驗證,就是對一個全局變量(對象變量)started做檢驗,如果爲true,則start()拋出異常,不會調用本地方法start0(),否則,先將該變量設有true,然後調用start0()。

  從中我們可以看到這個爲了控制一個線程對象只能運行成功一次start()方法.這是因爲線程的運行要獲取當前環境,包括安全,父線程的權限,優先級等條件,如果一個線程對象可以運行多次,那麼定義一個static 的線程在一個環境中獲取相應權限和優先級,運行完成後它在另一個環境中利用原來的權限和優先級等屬性在當前環境中運行,這樣就造成無法預知的結果.簡單說來,讓一個線程對象只能成功運行一次,是基於對線程管理的需要。

start()方法最本質的功能是從CPU中申請另一個線程空間來執行run()方法中的代碼,它和當前的線程是兩條線,在相對獨立的線程空間運行,也就是說,如果你直接調用線程對象的run()方法,當然也會執行,但那是在當前線程中執行,run()方法執行完成後繼續執行下面的代碼.而調用start()方法後,run()方法的代碼會和當前線程併發(單CPU)或並行(多CPU)執行。

  所以請記住一句話[調用線程對象的run方法不會產生一個新的線程],雖然可以達到相同的執行結果,但執行過程和執行效率不同。

  [線程的interrupt()方法,interrupted()和isInterrupted()]

  這三個方法是關係非常密切而且又比較複雜的,雖然它們各自的功能很清楚,但它們之間的關係有大多數人不是真正的瞭解。

  先說interrupt()方法,它是實例方法,而它也是最奇怪的方法,在java語言中,線程最初被設計爲"隱晦難懂"的東西,直到現在它的語義不沒有象它的名字那樣準確。大多數人以爲,一個線程象調用了interrupt()方法,那它對應的線程就應該被中斷而拋出異常,事實中,當一個線程對象調用interrupt()方法,它對應的線程並沒有被中斷,只是改變了它的中斷狀態。

  使當前線程的狀態變以中斷狀態,如果沒有其它影響,線程還會自己繼續執行。

  只有當線程執行到sleep,wait,join等方法時,或者自己檢查中斷狀態而拋出異常的情況下,線程纔會拋出異常。

如果線程對象調用interrupt()後它對應的線程就立即中斷,那麼interrupted()方法就不可能執行。

   因爲interrupted()方法是一個static方法,就是說只能在當前線程上調用,而如果一個線程interrupt()後它已經中斷了,那它又如何讓自己interrupted()?

  正因爲一個線程調用interrupt()後只是改變了中斷狀態,它可以繼續執行下去,在沒有調用sleep,wait,join等法或自己拋出異常之前,它就可以調用interrupted()來清除中斷狀態(還會原狀)interrupted()方法會檢查當前線程的中斷狀態,如果爲 "被中斷狀態"則改變當前線程爲"非中斷狀態"並返回true,如果爲"非中斷狀態"則返回false,它不僅檢查當前線程是否爲中斷狀態,而且在保證當前線程回來非中斷狀態,所以它叫"interrupted",是說中斷的狀態已經結束(到非中斷狀態了)isInterrupted()方法則僅僅檢查線程對象對應的線程是否是中斷狀態,並不改變它的狀態。

  目前大家只能先記住這三個方法的功能,只有真正深入到多線程編程實踐中,纔會體會到它們爲什麼是對象方法,爲什麼是類方法。

  線程到底什麼時候才被中斷拋出InterruptedException異常,我們將在提高篇中詳細討論。

  [sleep(),join(),yield()方法]

  在現在的環節中,我只能先說明這些方法的作用和調用原則,至於爲什麼,在基礎篇中無法深入,只能在提高篇中詳細說明。

  sleep()方法中是類方法,也就是對當前線程而言的,程序員不能指定某個線程去sleep,只能是當前線程執行到sleep()方法時,睡眠指定的時間(讓其它線程運行).事實上也只能是類方法,在當前線程上調用.試想如果你調用一個線程對象的sleep()方法,那麼這個對象對應的線程如果不是正在運行,它如何sleep()?所以只有當前線程,因爲它正在執行,你才能保證它可以調用sleep()方法。

  原則:[在同步方法中儘量不要調用線程的sleep()方法],或者簡單說,對於一般水平的程序員你基本不應該調用sleep()方法。

  join()方法,正如第一節所言,在一個線程對象上調用join方法,是當前線程等待這個線程對象對應的線程結束,比如有兩個工作,工作A要耗時10秒鐘,工作B要耗時10秒或更多。我們在程序中先生成一個線程去做工作B,然後做工作A。

  new?B().start();//做工作B

  A();//做工作A

  工作A完成後,下面要等待工作B的結果來進行處理.如果工作B還沒有完成我就不能進行下面的工作C,所以

  B?b?=?new?B();

  b.start();//做工作B

  A();//做工作A

  b.join();//等工作B完成。

  C();//繼續工作C。

  原則:[join是測試其它工作狀態的唯一正確方法],我見過很多人,甚至有的是博士生,在處理一項工作時如果另一項工作沒有完成,說讓當前工作線程sleep(x),我問他,你這個x是如何指定的,你怎麼知道是100毫秒而不是99毫秒或是101毫秒?其實這就是OnXXX事件的實質,我們不是要等多長時間纔去做什麼事,而是當等待的工作正好完成的時候去做。

  yield()方法也是類方法,只在當前線程上調用,理由同上,它主是讓當前線程放棄本次分配到的時間片原則:[不是非常必要的情況下,沒有理由調用它].調用這個方法不會提高任何效率,只是降低了CPU的總週期上面介紹的線程一些方法,基於(基礎篇)而言只能簡單提及.以後具體應用中我會結合實例詳細論述。

  線程本身的其它方法請參看API文檔.下一節介紹非線程的方法,但和線程密切相關的兩[三]個對象方法:

  [wait(),notify()/notifyAll()]

  這是在多線程中非常重要的方法。

轉載自dev2dev網友axman的go deep into java專欄。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章