<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    關注技術,關注生活

    任何事情只要開始去做,永遠不會太遲。
    posts - 5, comments - 23, trackbacks - 0, articles - 18
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    [轉]Java線程的缺陷

    Posted on 2006-12-09 13:50 errorfun 閱讀(375) 評論(0)  編輯  收藏 所屬分類: JAVA
    Java?語言的線程模型是此語言的一個最難另人滿意的部分。盡管?Java?語言本身就支持線程編程是件好事,但是它對線程的語法和類包的支持太少,只能適用于極小型的應用環境。

    關于?Java?線程編程的大多數書籍都長篇累牘地指出了?Java?線程模型的缺陷,并提供了解決這些問題的急救包(Band-Aid/邦迪創可貼)類庫。我稱這些類為急救包,是因為它們所能解決的問題本應是由?Java?語言本身語法所包含的。從長遠來看,以語法而不是類庫方法,將能產生更高效的代碼。這是因為編譯器和?Java?虛擬器?(JVM)?能一同優化程序代碼,而這些優化對于類庫中的代碼是很難或無法實現的。

    在我的《Taming?Java?Threads?》(請參閱?參考資料?)書中以及本文中,我進一步建議對?Java?編程語言本身進行一些修改,以使得它能夠真正解決這些線程編程的問題。本文和我這本書的主要區別是,我在撰寫本文時進行了更多的思考,?所以對書中的提議加以了提高。這些建議只是嘗試性的?--?只是我個人對這些問題的想法,而且實現這些想法需要進行大量的工作以及同行們的評價。但這是畢竟是一個開端,我有意為解決這些問題成立一個專門的工作組,如果您感興趣,請發?e-mail?到?
    threading@holub.com ?。一旦我真正著手進行,我就會給您發通知。

    這里提出的建議是非常大膽的。有些人建議對?Java?語言規范?(JLS)(請參閱參考資料?)進行細微和少量的修改以解決當前模糊的?JVM?行為,但是我卻想對其進行更為徹底的改進。

    在實際草稿中,我的許多建議包括為此語言引入新的關鍵字。雖然通常要求不要突破一個語言的現有代碼是正確的,但是如果該語言的并不是要保持不變以至于過時的話,它就必須能引入新的關鍵字。為了使引入的關鍵字與現有的標識符不產生沖突,經過細心考慮,我將使用一個?($)?字符,而這個字符在現有的標識符中是非法的。(例如,使用?$task,而不是?task)。此時需要編譯器的命令行開關提供支持,能使用這些關鍵字的變體,而不是忽略這個美元符號。

    task(任務)的概念

    Java?線程模型的根本問題是它完全不是面向對象的。面向對象?(OO)?設計人員根本不按線程角度考慮問題;他們考慮的是同步?信息?異步?信息(同步信息被立即處理?--?直到信息處理完成才返回消息句柄;異步信息收到后將在后臺處理一段時間?--?而早在信息處理結束前就返回消息句柄)。Java?編程語言中的?Toolkit.getImage()?方法就是異步信息的一個好例子。?getImage()?的消息句柄將被立即返回,而不必等到整個圖像被后臺線程取回。

    這是面向對象?(OO)?的處理方法。但是,如前所述,Java?的線程模型是非面向對象的。一個?Java?編程語言線程實際上只是一個run()?過程,它調用了其它的過程。在這里就根本沒有對象、異步或同步信息以及其它概念。

    對于此問題,在我的書中深入討論過的一個解決方法是,使用一個Active_object。?active?對象是可以接收異步請求的對象,它在接收到請求后的一段時間內以后臺方式得以處理。在?Java?編程語言中,一個請求可被封裝在一個對象中。例如,你可以把一個通過?Runnable?接口實現的實例傳送給此?active?對象,該接口的?run()?方法封裝了需要完成的工作。該?runnable?對象被此?active?對象排入到隊列中,當輪到它執行時,active?對象使用一個后臺線程來執行它。

    在一個?active?對象上運行的異步信息實際上是同步的,因為它們被一個單一的服務線程按順序從隊列中取出并執行。因此,使用一個?active?對象以一種更為過程化的模型可以消除大多數的同步問題。

    在某種意義上,Java?編程語言的整個?Swing/AWT?子系統是一個?active?對象。向一個?Swing?隊列傳送一條訊息的唯一安全的途徑是,調用一個類似SwingUtilities.invokeLater()?的方法,這樣就在?Swing?事件隊列上發送了一個?runnable?對象,當輪到它執行時,?Swing?事件處理線程將會處理它。

    那么我的第一個建議是,向?Java?編程語言中加入一個task?(任務)的概念,從而將active?對象集成到語言中。(?task的概念是從?Intel?的?RMX?操作系統和?Ada?編程語言借鑒過來的。大多數實時操作系統都支持類似的概念。)

    一個任務有一個內置的?active?對象分發程序,并自動管理那些處理異步信息的全部機制。

    定義一個任務和定義一個類基本相同,不同的只是需要在任務的方法前加一個asynchronous?修飾符來指示?active?對象的分配程序在后臺處理這些方法。請參考我的書中第九章的基于類方法,再看以下的?file_io?類,它使用了在《?Taming?Java?Threads?》中所討論的?Active_object?類來實現異步寫操作:

    所有的寫請求都用一個dispatch()?過程調用被放在?active-object?的輸入隊列中排隊。在后臺處理這些異步信息時出現的任何異常?(exception)?都由?Exception_handler?對象處理,此?Exception_handler?對象被傳送到?File_io_task?的構造函數中。您要寫內容到文件時,代碼如下:

    這種基于類的處理方法,其主要問題是太復雜了?--?對于一個這樣簡單的操作,代碼太雜了。向?Java?語言引入$task?和?$asynchronous?關鍵字后,就可以按下面這樣重寫以前的代碼:

    注意,異步方法并沒有指定返回值,因為其句柄將被立即返回,而不用等到請求的操作處理完成后。所以,此時沒有合理的返回值。對于派生出的模型,$task?關鍵字和?class?一樣同效:?$task?可以實現接口、繼承類和繼承的其它任務。標有?asynchronous?關鍵字的方法由?$task?在后臺處理。其它的方法將同步運行,就像在類中一樣。

    $task關鍵字可以用一個可選的?$error?從句修飾?(如上所示),它表明對任何無法被異步方法本身捕捉的異常將有一個缺省的處理程序。我使用?$?來代表被拋出的異常對象。如果沒有指定?$error?從句,就將打印出一個合理的出錯信息(很可能是堆棧跟蹤信息)。

    注意,為確保線程安全,異步方法的參數必須是不變?(immutable)?的。運行時系統應通過相關語義來保證這種不變性(簡單的復制通常是不夠的)。

    所有的?task?對象必須支持一些偽信息?(pseudo-message),例如:

    除了常用的修飾符(public?等),?task?關鍵字還應接受一個?$pooled(n)?修飾符,它導致?task?使用一個線程池,而不是使用單個線程來運行異步請求。?n?指定了所需線程池的大小;必要時,此線程池可以增加,但是當不再需要線程時,它應該縮到原來的大小。偽域?(pseudo-field)?$pool_size?返回在?$pooled(n)?中指定的原始?n?參數值。

    在《Taming?Java?Threads?》的第八章中,我給出了一個服務器端的?socket?處理程序,作為線程池的例子。它是關于使用線程池的任務的一個好例子。其基本思路是產生一個獨立對象,它的任務是監控一個服務器端的?socket。每當一個客戶機連接到服務器時,服務器端的對象會從池中抓取一個預先創建的睡眠線程,并把此線程設置為服務于客戶端連接。socket?服務器會產出一個額外的客戶服務線程,但是當連接關閉時,這些額外的線程將被刪除。實現?socket?服務器的推薦語法如下:

    Socket_server對象使用一個獨立的后臺線程處理異步的?listen()?請求,它封裝?socket?的"接受"循環。當每個客戶端連接時,?listen()?請求一個?Client_handler?通過調用?handle()?來處理請求。每個?handle()?請求在它們自己的線程中執行(因為這是一個?$pooled?任務)。

    注意,每個傳送到$pooled?$task?的異步消息實際上都使用它們自己的線程來處理。典型情況下,由于一個?$pooled?$task?用于實現一個自主操作;所以對于解決與訪問狀態變量有關的潛在的同步問題,最好的解決方法是在?$asynchronous?方法中使用?this?是指向的對象的一個獨有副本。這就是說,當向一個?$pooled?$task?發送一個異步請求時,將執行一個?clone()?操作,并且此方法的?this?指針會指向此克隆對象。線程之間的通信可通過對?static?區的同步訪問實現。


    改進synchronized

    雖然在多數情況下,$task?消除了同步操作的要求,但是不是所有的多線程系統都用任務來實現。所以,還需要改進現有的線程模塊。?synchronized?關鍵字有下列缺點:?無法指定一個超時值。?無法中斷一個正在等待請求鎖的線程。?無法安全地請求多個鎖?。(多個鎖只能以依次序獲得。)

    解決這些問題的辦法是:擴展synchronized?的語法,使它支持多個參數和能接受一個超時說明(在下面的括弧中指定)。下面是我希望的語法:

    synchronized(x?&&?y?&&?z)?獲得?x、y?和?z?對象的鎖。?
    synchronized(x?||?y?||?z)?獲得?x、y?或?z?對象的鎖。?
    synchronized(?(x?&&?y?)?||?z)?對于前面代碼的一些擴展。?
    synchronized(...)[1000]?設置?1?秒超時以獲得一個鎖。?
    synchronized[1000]?f(){...}?在進入?f()?函數時獲得?this?的鎖,但可有?1?秒超時。?

    TimeoutException是?RuntimeException?派生類,它在等待超時后即被拋出。

    超時是需要的,但還不足以使代碼強壯。您還需要具備從外部中止請求鎖等待的能力。所以,當向一個等待鎖的線程傳送一個interrupt()?方法后,此方法應拋出一個?SynchronizationException?對象,并中斷等待的線程。這個異常應是?RuntimeException?的一個派生類,這樣不必特別處理它。

    對synchronized?語法這些推薦的更改方法的主要問題是,它們需要在二進制代碼級上修改。而目前這些代碼使用進入監控(enter-monitor)和退出監控(exit-monitor)指令來實現?synchronized?。而這些指令沒有參數,所以需要擴展二進制代碼的定義以支持多個鎖定請求。但是這種修改不會比在?Java?2?中修改?Java?虛擬機的更輕松,但它是向下兼容現存的?Java?代碼。

    另一個可解決的問題是最常見的死鎖情況,在這種情況下,兩個線程都在等待對方完成某個操作。設想下面的一個例子(假設的):

    設想一個線程調用a()?,但在獲得 ?lock1?之后在獲得?lock2?之前被剝奪運行權。?第二個線程進入運行,調用?b()?,獲得了?lock2?,但是由于第一個線程占用?lock1?,所以它無法獲得?lock1?,所以它隨后處于等待狀態。此時第一個線程被喚醒,它試圖獲得?lock2?,但是由于被第二個線程占據,所以無法獲得。此時出現死鎖。下面的?synchronize-on-multiple-objects?的語法可解決這個問題:

    編譯器(或虛擬機)會重新排列請求鎖的順序,使lock1?總是被首先獲得,這就消除了死鎖。

    但是,這種方法對多線程不一定總成功,所以得提供一些方法來自動打破死鎖。一個簡單的辦法就是在等待第二個鎖時常釋放已獲得的鎖。這就是說,應采取如下的等待方式,而不是永遠等待:

    如果等待鎖的每個程序使用不同的超時值,就可打破死鎖而其中一個線程就可運行。我建議用以下的語法來取代前面的代碼:

    synchronized語句將永遠等待,但是它時常會放棄已獲得的鎖以打破潛在的死鎖可能。在理想情況下,每個重復等待的超時值比前一個相差一隨機值。

    改進wait()?和?notify()

    wait()/?notify()?系統也有一些問題:?無法檢測?wait()?是正常返回還是因超時返回。?無法使用傳統條件變量來實現處于一個"信號"(signaled)狀態。?太容易發生嵌套的監控(monitor)鎖定。

    超時檢測問題可以通過重新定義wait()?使它返回一個?boolean?變量?(而不是?void?)?來解決。一個?true?返回值指示一個正常返回,而?false?指示因超時返回。

    基于狀態的條件變量的概念是很重要的。如果此變量被設置成false?狀態,那么等待的線程將要被阻斷,直到此變量進入?true?狀態;任何等待?true?的條件變量的等待線程會被自動釋放。?(在這種情況下,?wait()?調用不會發生阻斷。)。通過如下擴展?notify()?的語法,可以支持這個功能:

    嵌套監控鎖定問題非常麻煩,我并沒有簡單的解決辦法。嵌套監控鎖定是一種死鎖形式,當某個鎖的占有線程在掛起其自身之前不釋放鎖時,會發生這種嵌套監控封鎖。下面是此問題的一個例子(還是假設的),但是實際的例子是非常多的:

    此例中,在get()?和?put()?操作中涉及兩個鎖:一個在?Stack?對象上,另一個在?LinkedList?對象上。下面我們考慮當一個線程試圖調用一個空棧的?pop()?操作時的情況。此線程獲得這兩個鎖,然后調用?wait()?釋放?Stack?對象上?的鎖,但是沒有釋放在?list?上的鎖。如果此時第二個線程試圖向堆棧中壓入一個對象,它會在?synchronized(list)?語句上永遠掛起,而且永遠不會被允許壓入一個對象。由于第一個線程等待的是一個非空棧,這樣就會發生死鎖。這就是說,第一個線程永遠無法從?wait()?返回,因為由于它占據著鎖,而導致第二個線程永遠無法運行到?notify()?語句。

    在這個例子中,有很多明顯的辦法來解決問題:例如,對任何的方法都使用同步。但是在真實世界中,解決方法通常不是這么簡單。

    一個可行的方法是,在wait()?中按照反順序釋放當前線程獲取的?所有?鎖,然后當等待條件滿足后,重新按原始獲取順序取得它們。但是,我能想象出利用這種方式的代碼對于人們來說簡直無法理解,所以我認為它不是一個真正可行的方法。如果您有好的方法,請給我發?e-mail。

    我也希望能等到下述復雜條件被實現的一天。例如:

    其中a?、?b?和?c?是任意對象。

    修改Thread?類

    同時支持搶占式和協作式線程的能力在某些服務器應用程序中是基本要求,尤其是在想使系統達到最高性能的情況下。我認為?Java?編程語言在簡化線程模型上走得太遠了,并且?Java?編程語言應支持?Posix/Solaris?的"綠色(green)線程"和"輕便(lightweight)進程"概念(在"(Taming?Java?Threads?"第一章中討論)。?這就是說,有些?Java?虛擬機的實現(例如在?NT?上的?Java?虛擬機)應在其內部仿真協作式進程,其它?Java?虛擬機應仿真搶占式線程。而且向?Java?虛擬機加入這些擴展是很容易的。

    一個?Java?的Thread?應始終是搶占式的。這就是說,一個?Java?編程語言的線程應像?Solaris?的輕便進程一樣工作。?Runnable?接口可以用于定義一個?Solaris?式的"綠色線程",此線程必需能把控制權轉給運行在相同輕便進程中的其它綠色線程。

    例如,目前的語法:

    能有效地為Runnable?對象產生一個綠色線程,并把它綁定到由?Thread?對象代表的輕便進程中。這種實現對于現有代碼是透明的,因為它的有效性和現有的完全一樣。

    把Runnable?對象想成為綠色線程,使用這種方法,只需向?Thread?的構造函數傳遞幾個?Runnable?對象,就可以擴展?Java?編程語言的現有語法,以支持在一個單一輕便線程有多個綠色線程。(綠色線程之間可以相互協作,但是它們可被運行在其它輕便進程?(?Thread?對象)?上的綠色進程(?Runnable?對象)?搶占。)。例如,下面的代碼會為每個?runnable?對象創建一個綠色線程,這些綠色線程會共享由?Thread?對象代表的輕便進程。

    現有的覆蓋(override)Thread?對象并實現?run()?的習慣繼續有效,但是它應映射到一個被綁定到一輕便進程的綠色線程。(在?Thread()?類中的缺省?run()?方法會在內部有效地創建第二個?Runnable?對象。)

    線程間的協作

    應在語言中加入更多的功能以支持線程間的相互通信。目前,PipedInputStream?和?PipedOutputStream?類可用于這個目的。但是對于大多數應用程序,它們太弱了。我建議向?Thread?類加入下列函數:?增加一個?wait_for_start()?方法,它通常處于阻塞狀態,直到一個線程的?run()?方法啟動。(如果等待的線程在調用?run?之前被釋放,這沒有什么問題)。用這種方法,一個線程可以創建一個或多個輔助線程,并保證在創建線程繼續執行操作之前,這些輔助線程會處于運行狀態。?(向?Object?類)增加?$send?(Object?o)?和?Object=$receive()?方法,它們將使用一個內部阻斷隊列在線程之間傳送對象。阻斷隊列應作為第一個?$send()?調用的副產品被自動創建。?$send()?調用會把對象加入隊列。?$receive()?調用通常處于阻塞狀態,直到有一個對象被加入隊列,然后它返回此對象。這種方法中的變量應支持設定入隊和出隊的操作超時能力:?$send?(Object?o,?long?timeout)?和?$receive?(long?timeout)。

    對于讀寫鎖的內部支持

    讀寫鎖的概念應內置到?Java?編程語言中。讀寫器鎖在"Taming?Java?Threads?"(和其它地方)中有詳細討論,概括地說:一個讀寫鎖支持多個線程同時訪問一個對象,但是在同一時刻只有一個線程可以修改此對象,并且在訪問進行時不能修改。讀寫鎖的語法可以借用?synchronized?關鍵字:

    對于一個對象,應該只有在$writing?塊中沒有線程時,才支持多個線程進入?$reading?塊。在進行讀操作時,一個試圖進入?$writing?塊的線程會被阻斷,直到讀線程退出?$reading?塊。?當有其它線程處于?$writing?塊時,試圖進入?$reading?或?$writing?塊的線程會被阻斷,直到此寫線程退出?$writing?塊。

    如果讀和寫線程都在等待,缺省情況下,讀線程會首先進行。但是,可以使用$writer_priority?屬性修改類的定義來改變這種缺省方式。如:
    訪問部分創建的對象應是非法的

    當前情況下,JLS?允許訪問部分創建的對象。例如,在一個構造函數中創建的線程可以訪問正被創建的對象,既使此對象沒有完全被創建。下面代碼的結果無法確定:

    設置x?為?-1?的線程可以和設置?x?為?0?的線程同時進行。所以,此時?x?的值無法預測。

    對此問題的一個解決方法是,在構造函數沒有返回之前,對于在此構造函數中創建的線程,既使它的優先級比調用new?的線程高,也要禁止運行它的?run()?方法。

    這就是說,在構造函數返回之前,start()?請求必須被推遲。

    另外,Java?編程語言應可允許構造函數的同步。換句話說,下面的代碼(在當前情況下是非法的)會象預期的那樣工作:

    我認為第一種方法比第二種更簡潔,但實現起來更為困難。

    volatile關鍵字應象預期的那樣工作

    JLS?要求保留對于?volatile?操作的請求。大多數?Java?虛擬機都簡單地忽略了這部分內容,這是不應該的。在多處理器的情況下,許多主機都出現了這種問題,但是它本應由?JLS?加以解決的。如果您對這方面感興趣,馬里蘭大學的?Bill?Pugh?正在致力于這項工作(請參閱參考資料?)。

    訪問的問題

    如果缺少良好的訪問控制,會使線程編程非常困難。大多數情況下,如果能保證線程只從同步子系統中調用,不必考慮線程安全(threadsafe)問題。我建議對?Java?編程語言的訪問權限概念做如下限制;應精確使用?package?關鍵字來限制包訪問權。我認為當缺省行為的存在是任何一種計算機語言的一個瑕疵,我對現在存在這種缺省權限感到很迷惑(而且這種缺省是"包(package)"級別的而不是"私有(private)")。在其它方面,Java?編程語言都不提供等同的缺省關鍵字。雖然使用顯式的?package?的限定詞會破壞現有代碼,但是它將使代碼的可讀性更強,并能消除整個類的潛在錯誤?(例如,如果訪問權是由于錯誤被忽略,而不是被故意忽略)。?重新引入?private?protected?,它的功能應和現在的?protected?一樣,但是不應允許包級別的訪問。?允許?private?private?語法指定"實現的訪問"對于所有外部對象是私有的,甚至是當前對象是的同一個類的。對于"."左邊的唯一引用(隱式或顯式)應是?this?。?擴展?public?的語法,以授權它可制定特定類的訪問。例如,下面的代碼應允許?Fred?類的對象可調用?some_method()?,但是對其它類的對象,這個方法應是私有的。

    這種建議不同于?C++?的?"friend"?機制。?在?"friend"?機制中,它授權一個類訪問另一個類的所有?私有部分。在這里,我建議對有限的方法集合進行嚴格控制的訪問。用這種方法,一個類可以為另一個類定義一個接口,而這個接口對系統的其余類是不可見的。一個明顯的變化是:

    除非域引用的是真正不變(immutable)的對象或static?final?基本類型,否則所有域的定義應是?private?。對于一個類中域的直接訪問違反了?OO?設計的兩個基本規則:抽象和封裝。從線程的觀點來看,允許直接訪問域只使對它進行非同步訪問更容易一些。

    增加$property?關鍵字。帶有此關鍵字的對象可被一個"bean?盒"應用程序訪問,這個程序使用在?Class?類中定義的反射操作(introspection)?API,否則與?private?private?同效。?$property?屬性可用在域和方法,這樣現有的?JavaBean?getter/setter?方法可以很容易地被定義為屬性。

    不變性(immutability)

    由于對不變對象的訪問不需要同步,所以在多線程條件下,不變的概念(一個對象的值在創建后不可更改)是無價的。Java?編程言語中,對于不變性的實現不夠嚴格,有兩個原因:對于一個不變對象,在其被未完全創建之前,可以對它進行訪問。這種訪問對于某些域可以產生不正確的值。?對于恒定?(類的所有域都是?final)?的定義太松散。對于由?final?引用指定的對象,雖然引用本身不能改變,但是對象本身可以改變狀態。

    第一個問題可以解決,不允許線程在構造函數中開始執行?(或者在構造函數返回之前不能執行開始請求)。

    對于第二個問題,通過限定final?修飾符指向恒定對象,可以解決此問題。這就是說,對于一個對象,只有所有的域是?final,并且所有引用的對象的域也都是?final,此對象才真正是恒定的。為了不打破現有代碼,這個定義可以使用編譯器加強,即只有一個類被顯式標為不變時,此類才是不變類。方法如下:

    有了$immutable?修飾符后,在域定義中的?final?修飾符是可選的。

    最后,當使用內部類(inner?class)后,在?Java?編譯器中的一個錯誤使它無法可靠地創建不變對象。當一個類有重要的內部類時(我的代碼常有),編譯器經常不正確地顯示下列錯誤信息:

    既使空的?final?在每個構造函數中都有初始化,還是會出現這個錯誤信息。自從在?1.1?版本中引入內部類后,編譯器中一直有這個錯誤。在此版本中(三年以后),這個錯誤依然存在。現在,該是改正這個錯誤的時候了。

    對于類級域的實例級訪問

    除了訪問權限外,還有一個問題,即類級(靜態)方法和實例(非靜態)方法都能直接訪問類級(靜態)域。這種訪問是非常危險的,因為實例方法的同步不會獲取類級的鎖,所以一個synchronized?static?方法和一個?synchronized?方法還是能同時訪問類的域。改正此問題的一個明顯的方法是,要求在實例方法中只有使用?static?訪問方法才能訪問非不變類的?static?域。當然,這種要求需要編譯器和運行時間檢查。在這種規定下,下面的代碼是非法的:

    由于f()?和?g()?可以并行運行,所以它們能同時改變?x?的值(產生不定的結果)。請記住,這里有兩個鎖:?static?方法要求屬于?Class?對象的鎖,而非靜態方法要求屬于此類實例的鎖。當從實例方法中訪問非不變?static?域時,編譯器應要求滿足下面兩個結構中的任意一個:

    或則,編譯器應獲得讀/寫鎖的使用:

    另外一種方法是(這也是一種理想的?方法)--?編譯器應?自動?使用一個讀/寫鎖來同步訪問非不變?static?域,這樣,程序員就不必擔心這個問題。

    后臺線程的突然結束

    當所有的非后臺線程終止后,后臺線程都被突然結束。當后臺線程創建了一些全局資源(例如一個數據庫連接或一個臨時文件),而后臺線程結束時這些資源沒有被關閉或刪除就會導致問題。

    對于這個問題,我建議制定規則,使?Java?虛擬機在下列情況下不關閉應用程序:有任何非后臺線程正在運行,或者:?有任何后臺線程正在執行一個?synchronized?方法或?synchronized?代碼塊。

    后臺線程在它執行完synchronized?塊或?synchronized?方法后可被立即關閉。

    重新引入stop()?、?suspend()?和?resume()?關鍵字

    由于實用原因這也許不可行,但是我希望不要廢除stop()?(在?Thread?和?ThreadGroup?中)。但是,我會改變?stop()?的語義,使得調用它時不會破壞已有代碼。但是,關于?stop()?的問題,請記住,當線程終止后,?stop()?將釋放所有鎖,這樣可能潛在地使正在此對象上工作的線程進入一種不穩定(局部修改)的狀態。由于停止的線程已釋放它在此對象上的所有鎖,所以這些對象無法再被訪問。

    對于這個問題,可以重新定義stop()?的行為,使線程只有在不占有任何鎖時才立即終止。如果它占據著鎖,我建議在此線程釋放最后一個鎖后才終止它。可以使用一個和拋出異常相似的機制來實現此行為。被停止線程應設置一個標志,并且當退出所有同步塊時立即測試此標志。如果設置了此標志,就拋出一個隱式的異常,但是此異常應不再能被捕捉并且當線程結束時不會產生任何輸出。注意,微軟的?NT?操作系統不能很好地處理一個外部指示的突然停止(abrupt)。(它不把?stop?消息通知動態連接庫,所以可能導致系統級的資源漏洞。)這就是我建議使用類似異常的方法簡單地導致?run()?返回的原因。

    與這種和異常類似的處理方法帶來的實際問題是,你必需在每個synchronized?塊后都插入代碼來測試"stopped"標志。并且這種附加的代碼會降低系統性能并增加代碼長度。我想到的另外一個辦法是使?stop()?實現一個"延遲的(lazy)"停止,在這種情況下,在下次調用?wait()?或?yield()?時才終止。我還想向?Thread?中加入一個?isStopped()?和?stopped()?方法(此時,?Thread?將像?isInterrupted()?和?interrupted()?一樣工作,但是會檢測?"stop-requested"的狀態)。這種方法不向第一種那樣通用,但是可行并且不會產生過載。

    應把suspend()?和?resume()?方法放回到?Java?編程語言中,它們是很有用的,我不想被當成是幼兒園的小孩。由于它們可能產生潛在的危險(當被掛起時,一個線程可以占據一個鎖)而去掉它們是沒有道理的。請讓我自己來決定是否使用它們。如果接收的線程正占據著鎖,Sun?公司應該把它們作為調用?suspend()?的一個運行時間異常處理(run-time?exception);或者更好的方法是,延遲實際的掛起過程,直到線程釋放所有的鎖。

    被阻斷的?I/O?應正確工作

    應該能打斷任何被阻斷的操作,而不是只讓它們wait()?和?sleep()?。我在"?Taming?Java?Threads?"的第二章中的?socket?部分討論了此問題。但是現在,對于一個被阻斷的?socket?上的?I/O?操作,打斷它的唯一辦法是關閉這個?socket,而沒有辦法打斷一個被阻斷的文件?I/O?操作。例如,一旦開始一個讀請求并且進入阻斷狀態后,除非到它實際讀出一些東西,否則線程一直出于阻斷狀態。既使關掉文件句柄也不能打斷讀操作。

    還有,程序應支持?I/O?操作的超時。所有可能出現阻斷操作的對象(例如?InputStream?對象)也都應支持這種方法:

    這和?Socket?類的setSoTimeout(time)?方法是等價的。同樣地,應該支持把超時作為參數傳遞到阻斷的調用。

    ThreadGroup類

    ThreadGroup應該實現?Thread?中能夠改變線程狀態的所有方法。我特別想讓它實現?join()?方法,這樣我就可等待組中的所有線程的終止。

    總結

    以上是我的建議。就像我在標題中所說的那樣,如果我是國王...(哎)。我希望這些改變(或其它等同的方法)最終能被引入?Java?語言中。我確實認為?Java?語言是一種偉大的編程語言;但是我也認為?Java?的線程模型設計得還不夠完善,這是一件很可惜的事情。但是,Java?編程語言正在演變,所以還有可提高的前景。

    參考資料本文是對?Taming?Java?Threads?的更新摘編。該書探討了在?Java?語言中多線程編程的陷阱和問題,并提供了一個與線程相關的?Java?程序包來解決這些問題。?馬里蘭大學的?Bill?Pugh?正在致力修改?JLS?來提高其線程模型。Bill?的提議并不如本文所推薦的那么廣,他主要致力于讓現有的線程模型以更為合理方式運行。更多信息可從?www.cs.umd.edu/~pugh/java/memoryModel/?獲得。?從?Sun?網站?可找到全部?Java?語言的規范。?要從一個純技術角度來審視線程,參閱?Doug?Lea?編著的?Concurrent?Programming?in?Java:?Design?Principles?and?Patterns?第二版?。這是本很棒的書,但是它的風格是非常學術化的并不一定適合所有的讀者。對《?Taming?Java?Threads?》是個很好的補充讀物。?由?Scott?Oaks?和?Henry?Wong?編寫的?Java?Threads?比?Taming?Java?Threads?要輕量些,但是如果您從未編寫過線程程序這本書更為適合。Oaks?和?Wong?同樣實現了?Holub?提供的幫助類,而且看看對同一問題的不同解決方案總是有益的。?由?Bill?Lewis?和?Daniel?J.?Berg 編寫的?Threads?Primer:?A?Guide?to?Multithreaded?Programming?是對線程(不限于?Java)的很好入門介紹。?Java?線程的一些技術信息可在?Sun?網站?上找到。?在?"Multiprocessor?Safety?and?Java"?中?Paul?Jakubik?討論了多線程系統的?SMP?問題

    主站蜘蛛池模板: 一边摸一边桶一边脱免费视频| 久久亚洲AV成人出白浆无码国产| 亚洲一区动漫卡通在线播放| 久草免费手机视频| 五月天网站亚洲小说| 可以免费观看的国产视频| 亚洲AV无码专区亚洲AV伊甸园| 黄色片免费在线观看| 亚洲福利视频导航| 中文字幕免费在线| 亚洲日本va在线观看| 免费毛片在线视频| 高潮内射免费看片| 久久久亚洲精品蜜桃臀| 成人一区二区免费视频| 亚洲av午夜福利精品一区人妖| 永久在线观看www免费视频| 亚洲不卡视频在线观看| 免费无码不卡视频在线观看 | 亚洲高清无在码在线电影不卡| 最近免费字幕中文大全视频 | 国产成人综合亚洲绿色| 亚洲国产成人精品无码久久久久久综合| 十八禁的黄污污免费网站| 亚洲伊人久久精品影院| 67pao强力打造高清免费| 亚洲乱亚洲乱妇24p| 亚洲精品成人在线| 99久久免费中文字幕精品| 国产亚洲精品影视在线| 亚洲免费日韩无码系列| 最近免费中文字幕mv电影| 精品无码专区亚洲| 久久精品国产亚洲av麻豆| 成人免费无码大片a毛片软件 | 亚洲精品国精品久久99热| 四虎国产成人永久精品免费| 亚洲AV无码专区国产乱码不卡| 国产亚洲福利精品一区| 日韩精品视频免费观看| 一个人免费视频观看在线www|