乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Java 理論與實踐: 并發(fā)在一定程度上使一切變得簡單

       raydian 2007-01-10
      對于每個項目,象許多其它應(yīng)用程序基礎(chǔ)結(jié)構(gòu)服務(wù)一樣,通常無需從頭重新編寫并發(fā)實用程序類(如工作隊列和線程池)。這個月,Brian Goetz 將介紹 Doug Lea 的 util.concurrent 包,這是一個高質(zhì)量的、廣泛使用的、并發(fā)實用程序的開放源碼包??梢酝ㄟ^本文的 論壇提出您對本文的想法,以饗筆者和其他讀者。(您也可以單擊本文頂部或底部的“討論”參加論壇。)

      當(dāng)項目中需要 XML 解析器、文本索引程序和搜索引擎、正則表達(dá)式編譯器、XSL 處理器或 PDF 生成器時,我們中大多數(shù)人從不會考慮自己去編寫這些實用程序。每當(dāng)需要這些設(shè)施時,我們會使用商業(yè)實現(xiàn)或開放源碼實現(xiàn)來執(zhí)行這些任務(wù)原因很簡單 ― 現(xiàn)有實現(xiàn)工作得很好,而且易于使用,自己編寫這些實用程序會事倍功半,或者甚至得不到結(jié)果。作為軟件工程師,我們更愿意遵循艾薩克?牛頓的信念 ― 站在巨人的肩膀之上,有時這是可取的,但并不總是這樣。(在 Richard Hamming 的 Turing Award 講座中,他認(rèn)為計算機科學(xué)家的“自立”要更可取。)

      探究重復(fù)發(fā)明“車輪”之原因

      對于一些幾乎每個服務(wù)器應(yīng)用程序都需要的低級應(yīng)用程序框架服務(wù)(如日志記錄、數(shù)據(jù)庫連接合用、高速緩存和任務(wù)調(diào)度等),我們看到這些基本的基礎(chǔ)結(jié)構(gòu)服務(wù)被一遍又一遍地重寫。為什么會發(fā)生這種情況?因為現(xiàn)有的選擇不夠充分,或者因為定制版本要更好些或更適合手邊的應(yīng)用程序,但我認(rèn)為這是不必要的。事實上,專為某個應(yīng)用程序開發(fā)的定制版本常常并不比廣泛可用的、通用的實現(xiàn)更適合于該應(yīng)用程序,也許會更差。例如,盡管您不喜歡 log4j,但它可以完成任務(wù)。盡管自己開發(fā)的日志記錄系統(tǒng)也許有一些 log4j 所缺乏的特定特性,但對于大多數(shù)應(yīng)用程序,您很難證明,一個完善的定制日志記錄包值得付出從頭編寫的代價,而不使用現(xiàn)有的、通用的實現(xiàn)。可是,許多項目團隊最終還是自己一遍又一遍地編寫日志記錄、連接合用或線程調(diào)度包。

      表面上看起來簡單

      我們不考慮自己去編寫 XSL 處理器的原因之一是,這將花費大量的工作。但這些低級的框架服務(wù)表面上看起來簡單,所以自己編寫它們似乎并不困難。然而,它們很難正常工作,并不象開始看起來那樣。這些特殊的“輪子”一直處在重復(fù)發(fā)明之中的主要原因是,在給定的應(yīng)用程序中,往往一開始對這些工具的需求非常小,但當(dāng)您遇到了無數(shù)其它項目中也存在的同樣問題時,這種需求會逐漸變大。理由通常象這樣:“我們不需要完善的日志記錄/調(diào)度/高速緩存包,只需要一些簡單的包,所以只編寫一些能達(dá)到我們目的的包,我們將針對自己特定的需求來調(diào)整它”。但情況往往是,您很快擴展了所編寫的這個簡單工具,并試圖添加再添加更多的特性,直到編寫出一個完善的基礎(chǔ)結(jié)構(gòu)服務(wù)。至此,您通常會執(zhí)著于自己所編寫的程序,無論它是好是壞。您已經(jīng)為構(gòu)建自己的程序付出了全部的代價,所以除了轉(zhuǎn)至通用的實現(xiàn)所實際投入的遷移成本之外,還必須克服這種“已支付成本”的障礙。





      回頁首


      并發(fā)構(gòu)件的價值所在

      編寫調(diào)度和并發(fā)基礎(chǔ)結(jié)構(gòu)類的確要比看上去難。Java 語言提供了一組有用的低級同步原語: wait() 、 notify()synchronized ,但具體使用這些原語需要一些技巧,需要考慮性能、死鎖、公平性、資源管理以及如何避免線程安全性方面帶來的危害等諸多因素。并發(fā)代碼難以編寫,更難以測試 ― 即使專家有時在第一次時也會出現(xiàn)錯誤。 Concurrent Programming in Java(請參閱 參考資料)的作者 Doug Lea 編寫了一個極其優(yōu)秀的、免費的并發(fā)實用程序包,它包括并發(fā)應(yīng)用程序的鎖、互斥、隊列、線程池、輕量級任務(wù)、有效的并發(fā)集合、原子的算術(shù)操作和其它基本構(gòu)件。人們一般稱這個包為 util.concurrent (因為它實際的包名很長),該包將形成 Java Community Process JSR 166 正在標(biāo)準(zhǔn)化的 JDK 1.5 中 java.util.concurrent 包的基礎(chǔ)。同時, util.concurrent 經(jīng)過了良好的測試,許多服務(wù)器應(yīng)用程序(包括 JBoss J2EE 應(yīng)用程序服務(wù)器)都使用這個包。

      填補空白

      核心 Java 類庫中略去了一組有用的高級同步工具(譬如互斥、信號和阻塞、線程安全集合類)。Java 語言的并發(fā)原語 ― synchronization 、 wait()notify() ― 對于大多數(shù)服務(wù)器應(yīng)用程序的需求而言過于低級。如果要試圖獲取鎖,但如果在給定的時間段內(nèi)超時了還沒有獲得它,會發(fā)生什么情況?如果線程中斷了,則放棄獲取鎖的嘗試?創(chuàng)建一個至多可有 N 個線程持有的鎖?支持多種方式的鎖定(譬如帶互斥寫的并發(fā)讀)?或者以一種方式來獲取鎖,但以另一種方式釋放它?內(nèi)置的鎖定機制不直接支持上述這些情形,但可以在 Java 語言所提供的基本并發(fā)原語上構(gòu)建它們。但是這樣做需要一些技巧,而且容易出錯。

      服務(wù)器應(yīng)用程序開發(fā)人員需要簡單的設(shè)施來執(zhí)行互斥、同步事件響應(yīng)、跨活動的數(shù)據(jù)通信以及異步地調(diào)度任務(wù)。對于這些任務(wù),Java 語言所提供的低級原語很難用,而且容易出錯。 util.concurrent 包的目的在于通過提供一組用于鎖定、阻塞隊列和任務(wù)調(diào)度的類來填補這項空白,從而能夠處理一些常見的錯誤情況或者限制任務(wù)隊列和運行中的任務(wù)所消耗的資源。





      回頁首


      調(diào)度異步任務(wù)

      util.concurrent 中使用最廣泛的類是那些處理異步事件調(diào)度的類。在本專欄七月份的文章中,我們研究了 thread pools and work queues,以及許多 Java 應(yīng)用程序是如何使用“ Runnable 隊列”模式調(diào)度小工作單元。

      可以通過簡單地為某個任務(wù)創(chuàng)建一個新線程來派生執(zhí)行該任務(wù)的后端線程,這種做法很吸引人:

      new Thread(new Runnable() { ... } ).start();
                  

      雖然這種做法很好,而且很簡潔,但有兩個重大缺陷。首先,創(chuàng)建新的線程需要耗費一定資源,因此產(chǎn)生出許許多多線程,每個將執(zhí)行一個簡短的任務(wù),然后退出,這意味著 JVM 也許要做更多的工作,創(chuàng)建和銷毀線程而消耗的資源比實際做有用工作所消耗的資源要多。即使創(chuàng)建和銷毀線程的開銷為零,這種執(zhí)行模式仍然有第二個更難以解決的缺陷 ― 在執(zhí)行某類任務(wù)時,如何限制所使用的資源?如果突然到來大量的請求,如何防止同時會產(chǎn)生大量的線程?現(xiàn)實世界中的服務(wù)器應(yīng)用程序需要比這更小心地管理資源。您需要限制同時執(zhí)行異步任務(wù)的數(shù)目。

      線程池解決了以上兩個問題 — 線程池具有可以同時提高調(diào)度效率和限制資源使用的好處。雖然人們可以方便地編寫工作隊列和用池線程執(zhí)行 Runnable 的線程池(七月份那篇專欄文章中的示例代碼正是用于此目的),但編寫有效的任務(wù)調(diào)度程序需要做比簡單地同步對共享隊列的訪問更多的工作?,F(xiàn)實世界中的任務(wù)調(diào)度程序應(yīng)該可以處理死線程,殺死超量的池線程,使它們不消耗不必要的資源,根據(jù)負(fù)載動態(tài)地管理池的大小,以及限制排隊任務(wù)的數(shù)目。為了防止服務(wù)器應(yīng)用程序在過載時由于內(nèi)存不足錯誤而造成崩潰,最后一項(即限制排隊的任務(wù)數(shù)目)是很重要的。

      限制任務(wù)隊列需要做決策 ― 如果工作隊列溢出,則如何處理這種溢出?拋棄最新的任務(wù)?拋棄最老的任務(wù)?阻塞正在提交的線程直到隊列有可用的空間?在正在提交的線程內(nèi)執(zhí)行新的任務(wù)?存在著各種切實可行的溢出管理策略,每種策略都會在某些情形下適合,而在另一些情形下不適合。





      回頁首


      Executor

      Util.concurrent 定義一個 Executor 接口,以異步地執(zhí)行 Runnable ,另外還定義了 Executor 的幾個實現(xiàn),它們具有不同的調(diào)度特征。將一個任務(wù)排入 executor 的隊列非常簡單:

      Executor executor = new QueuedExecutor();
                  ...
                  Runnable runnable = ... ;
                  executor.execute(runnable);
                  

      最簡單的實現(xiàn) ThreadedExecutor 為每個 Runnable 創(chuàng)建了一個新線程,這里沒有提供資源管理 ― 很象 new Thread(new Runnable() {}).start() 這個常用的方法。但 ThreadedExecutor 有一個重要的好處:通過只改變 executor 結(jié)構(gòu),就可以轉(zhuǎn)移到其它執(zhí)行模型,而不必緩慢地在整個應(yīng)用程序源碼內(nèi)查找所有創(chuàng)建新線程的地方。 QueuedExecutor 使用一個后端線程來處理所有任務(wù),這非常類似于 AWT 和 Swing 中的事件線程。 QueuedExecutor 具有一個很好的特性:任務(wù)按照排隊的順序來執(zhí)行,因為是在一個線程內(nèi)來執(zhí)行所有的任務(wù),任務(wù)無需同步對共享數(shù)據(jù)的所有訪問。

      PooledExecutor 是一個復(fù)雜的線程池實現(xiàn),它不但提供工作線程(worker thread)池中任務(wù)的調(diào)度,而且還可靈活地調(diào)整池的大小,同時還提供了線程生命周期管理,這個實現(xiàn)可以限制工作隊列中任務(wù)的數(shù)目,以防止隊列中的任務(wù)耗盡所有可用內(nèi)存,另外還提供了多種可用的關(guān)閉和飽和度策略(阻塞、廢棄、拋出、廢棄最老的、在調(diào)用者中運行等)。所有的 Executor 實現(xiàn)為您管理線程的創(chuàng)建和銷毀,包括當(dāng)關(guān)閉 executor 時,關(guān)閉所有線程,另外還為線程創(chuàng)建過程提供了 hook,以便應(yīng)用程序可以管理它希望管理的線程實例化。例如,這使您可以將所有工作線程放在特定的 ThreadGroup 中,或者賦予它們描述性名稱。





      回頁首


      FutureResult

      有時您希望異步地啟動一個進(jìn)程,同時希望在以后需要這個進(jìn)程時,可以使用該進(jìn)程的結(jié)果。 FutureResult 實用程序類使這變得很容易。 FutureResult 表示可能要花一段時間執(zhí)行的任務(wù),并且可以在另一個線程中執(zhí)行此任務(wù), FutureResult 對象可用作執(zhí)行進(jìn)程的句柄。通過它,您可以查明該任務(wù)是否已經(jīng)完成,可以等待任務(wù)完成,并檢索其結(jié)果??梢詫?FutureResultExecutor 組合起來;可以創(chuàng)建一個 FutureResult 并將其排入 executor 的隊列,同時保留對 FutureResult 的引用。清單 1 顯示了一個一同使用 FutureResultExecutor 的簡單示例,它異步地啟動圖像著色,并繼續(xù)進(jìn)行其它處理:


      清單 1. 運作中的 FutureResult 和 Executor
        Executor executor = ...
                  ImageRenderer renderer = ...
                  FutureResult futureImage = new FutureResult();
                  Runnable command = futureImage.setter(new Callable() {
                  public Object call() { return renderer.render(rawImage); }
                  });
                  // start the rendering process
                  executor.execute(command);
                  // do other things while executing
                  drawBorders();
                  drawCaption();
                  // retrieve the future result, blocking if necessary
                  drawImage((Image)(futureImage.get())); // use future
                  

      FutureResult 和高速緩存

      還可以使用 FutureResult 來提高按需裝入高速緩存的并發(fā)性。通過將 FutureResult 放置在高速緩存內(nèi),而不是放置計算本身的結(jié)果,可以減少持有高速緩存上寫鎖的時間。雖然這種做法不能加快第一個線程把某一項放入高速緩存,但它 減少第一個線程阻塞其它線程訪問高速緩存的時間。它還使其它線程更早地使用結(jié)果,因為它們可以從高速緩存中檢索 FutureTask 。清單 2 顯示了使用用于高速緩存的 FutureResult 示例:


      清單 2. 使用 FutureResult 來改善高速緩存
      public class FileCache {
                  private Map cache = new HashMap();
                  private Executor executor = new PooledExecutor();
                  public void get(final String name) {
                  FutureResult result;
                  synchronized(cache) {
                  result = cache.get(name);
                  if (result == null) {
                  result = new FutureResult();
                  executor.execute(result.setter(new Callable() {
                  public Object call() { return loadFile(name); }
                  }));
                  cache.put(result);
                  }
                  }
                  return result.get();
                  }
                  }
                  

      這種方法使第一個線程快速地進(jìn)入和退出同步塊,使其它線程與第一個線程一樣快地得到第一個線程計算的結(jié)果,不可能出現(xiàn)兩個線程都試圖計算同一個對象。





      回頁首


      結(jié)束語

      util.concurrent 包包含許多有用的類,您可能認(rèn)為其中一些類與您已編寫的類一樣好,也許甚至比從前還要好。它們是許多多線程應(yīng)用程序的基本構(gòu)件的高性能實現(xiàn),并經(jīng)歷了大量測試。 util.concurrent 是 JSR 166 的切入點,它將帶來一組并發(fā)性的實用程序,這些實用程序?qū)⒊蔀?JDK 1.5 中的 java.util.concurrent 包,但您不必等到那時侯才能使用它。在以后的文章中,我將討論 util.concurrent 中一些定制的同步類,并研究 util.concurrentjava.util.concurrent API 中的不同之處。


        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多