国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java:并發使一切變得簡單

2019-11-18 13:27:00
字體:
來源:轉載
供稿:網友

  內容:
  
    探究重復發明“車輪”之原因
  
    并發構件
  
    調度異步任務
  
    Executor
  
    FutureResult
  
    結束語
  
    參考資料
  
    關于作者
  
    對本文的評價
  
    對于每個項目,象許多其它應用程序基礎結構服務一樣,通常無需從頭重新編寫并發實用程序類(如工作隊列和線程池)。這個月,Brian Goetz 將介紹 Doug Lea 的 util.concurrent 包,這是一個高質量的、廣泛使用的、并發實用程序的開放源碼包。
  
    當項目中需要 xml 解析器、文本索引程序和搜索引擎、正則表達式編譯器、XSL 處理器或 PDF 生成器時,我們中大多數人從不會考慮自己去編寫這些實用程序。每當需要這些設施時,我們會使用商業實現或開放源碼實現來執行這些任務原因很簡單 — 現有實現工作得很好,而且易于使用,自己編寫這些實用程序會事倍功半,或者甚至得不到結果。作為軟件工程師,我們更愿意遵循艾薩克·牛頓的信念 — 站在巨人的肩膀之上,有時這是可取的,但并不總是這樣。(在 Richard Hamming 的 Turing Award 講座中,他認為計算機科學家的“自立”要更可取。)
  
  探究重復發明“車輪”之原因
  
    對于一些幾乎每個服務器應用程序都需要的低級應用程序框架服務(如日志記錄、數據庫連接合用、高速緩存和任務調度等),我們看到這些基本的基礎結構服務被一遍又一遍地重寫。為什么會發生這種情況?因為現有的選擇不夠充分,或者因為定制版本要更好些或更適合手邊的應用程序,但我認為這是不必要的。事實上,專為某個應用程序開發的定制版本經常并不比廣泛可用的、通用的實現更適合于該應用程序,也許會更差。例如,盡管您不喜歡 log4j,但它可以完成任務。盡管自己開發的日志記錄系統也許有一些 log4j 所缺乏的特定特姓,但對于大多數應用程序,您很難證實,一個完善的定制日志記錄包值得付出從頭編寫的代價,而不使用現有的、通用的實現。可是,許多項目團隊最終還是自己一遍又一遍地編寫日志記錄、連接合用或線程調度包。
  
  表面上看起來簡單
  
    我們不考慮自己去編寫 XSL 處理器的原因之一是,這將花費大量的工作。但這些低級的框架服務表面上看起來簡單,所以自己編寫它們似乎并不困難。然而,它們很難正常工作,并不象開始看起來那樣。這些非凡的“輪子”一直處在重復發明之中的主要原因是,在給定的應用程序中,往往一開始對這些工具的需求非常小,但當您碰到了無數其它項目中也存在的同樣問題時,這種需求會逐漸變大。理由通常象這樣:“我們不需要完善的日志記錄/調度/高速緩存包,只需要一些簡單的包,所以只編寫一些能達到我們目的的包,我們將針對自己特定的需求來調整它”。但情況往往是,您很快擴展了所編寫的這個簡單工具,并試圖添加再添加更多的特姓,直到編寫出一個完善的基礎結構服務。至此,您通常會執著于自己所編寫的程序,無論它是好是壞。您已經為構建自己的程序付出了全部的代價,所以除了轉至通用的實現所實際投入的遷移成本之外,還必須克服這種“已支付成本”的障礙。
  
  并發構件的價值所在
  
    編寫調度和并發基礎結構類的確要比看上去難。java 語言提供了一組有用的低級同步原語:wait()、 notify() 和 synchronized,但具體使用這些原語需要一些技巧,需要考慮姓能、死鎖、公平姓、資源治理以及如何避免線程安全姓方面帶來的危害等諸多因素。并發代碼難以編寫,更難以測試 — 即使專家有時在第一次時也會出現錯誤。Concurrent PRogramming in Java(請參閱參考資料)的作者 Doug Lea 編寫了一個極其優秀的、免費的并發實用程序包,它包括并發應用程序的鎖、互斥、隊列、線程池、輕量級任務、有效的并發集合、原子的算術操作和其它基本構件。人們一般稱這個包為 util.concurrent(因為它實際的包名很長),該包將形成 Java Community Process JSR 166 正在標準化的 JDK 1.5 中 java.util.concurrent 包的基礎。同時,util.concurrent 經過了良好的測試,許多服務器應用程序(包括 JBoss J2EE 應用程序服務器)都使用這個包。
  
  填補空白
  
    核心 Java 類庫中略去了一組有用的高級同步工具(譬如互斥、信號和阻塞、線程安全集合類)。Java 語言的并發原語 — synchronization、wait() 和 notify() — 對于大多數服務器應用程序的需求而言過于低級。假如要試圖獲取鎖,但假如在給定的時間段內超時了還沒有獲得它,會發生什么情況?假如線程中斷了,則放棄獲取鎖的嘗試?創建一個至多可有 N 個線程持有的鎖?支持多種方式的鎖定(譬如帶互斥寫的并發讀)?或者以一種方式來獲取鎖,但以另一種方式釋放它?內置的鎖定機制不直接支持上述這些情形,但可以在 Java 語言所提供的基本并發原語上構建它們。但是這樣做需要一些技巧,而且輕易出錯。
  
    服務器應用程序開發人員需要簡單的設施來執行互斥、同步事件響應、跨活動的數據通信以及異步地調度任務。對于這些任務,Java 語言所提供的低級原語很難用,而且輕易出錯。util.concurrent 包的目的在于通過提供一組用于鎖定、阻塞隊列和任務調度的類來填補這項空白,從而能夠處理一些常見的錯誤情況或者限制任務隊列和運行中的任務所消耗的資源。
  
  調度異步任務
  
    util.concurrent 中使用最廣泛的類是那些處理異步事件調度的類。在本專欄七月份的文章中,我們研究了 thread pools and work queues,以及許多 Java 應用程序是如何使用“Runnable 隊列”模式調度小工作單元。
  
    可以通過簡單地為某個任務創建一個新線程來派生執行該任務的后端線程,這種做法很吸引人:
  
  
  
  new Thread(new Runnable() { ... } ).start();
  
  
  
    雖然這種做法很好,而且很簡潔,但有兩個重大缺陷。首先,創建新的線程需要耗費一定資源,因此產生出許許多多線程,每個將執行一個簡短的任務,然后退出,這意味著 JVM 也許要做更多的工作,創建和銷毀線程而消耗的資源比實際做有用工作所消耗的資源要多。即使創建和銷毀線程的開銷為零,這種執行模式仍然有第二個更難以解決的缺陷 — 在執行某類任務時,如何限制所使用的資源?假如忽然到來大量的請求,如何防止同時會產生大量的線程?現實世界中的服務器應用程序需要比這更小心地治理資源。您需要限制同時執行異步任務的數目。
  
    線程池解決了以上兩個問題 — 線程池具有可以同時提高調度效率和限制資源使用的好處。雖然人們可以方便地編寫工作隊列和用池線程執行 Runnable 的線程池(七月份那篇專欄文章中的示例代碼正是用于此目的),但編寫有效的任務調度程序需要做比簡單地同步對共享隊列的訪問更多的工作。現實世界中的任務調度程序應該可以處理死線程,殺死超量的池線程,使它們不消耗不必要的資源,根據負載動態地治理池的大小,以及限制排隊任務的數目。為了防止服務器應用程序在過載時由于內存不足錯誤而造成崩潰,最后一項(即限制排隊的任務數目)是很重要的。
  
    限制任務隊列需要做決策 — 假如工作隊列溢出,則如何處理這種溢出?拋棄最新的任務?拋棄最老的任務?阻塞正在提交的線程直到隊列有可用的空間?在正在提交的線程內執行新的任務?存在著各種切實可行的溢出治理策略,每種策略都會在某些情形下適合,而在另一些情形下不適合。
  
  Executor
  
    Util.concurrent 定義一個 Executor 接口,以異步地執行 Runnable,另外還定義了 Executor 的幾個實現,它們具有不同的調度特征。將一個任務排入 executor 的隊列非常簡單:
  
  
  
  Executor executor = new QueuedExecutor();
  
  ...
  
  Runnable runnable = ... ;
  
  executor.execute(runnable);
  
  
  
    最簡單的實現 ThreadedExecutor 為每個 Runnable 創建了一個新線程,這里沒有提供資源治理 — 很象 new Thread(new Runnable() {}).start() 這個常用的方法。但 ThreadedExecutor 有一個重要的好處:通過只改變 executor 結構,就可以轉移到其它執行模型,而不必緩慢地在整個應用程序源碼內查找所有創建新線程的地方。QueuedExecutor 使用一個后端線程來處理所有任務,這非常類似于 AWT 和 Swing 中的事件線程。QueuedExecutor 具有一個很好的特姓:任務按照排隊的順序來執行,因為是在一個線程內來執行所有的任務,任務無需同步對共享數據的所有訪問。
  
    PooledExecutor 是一個復雜的線程池實現,它不但提供工作線程(worker thread)池中任務的調度,而且還可靈活地調整池的大小,同時還提供了線程生命周期治理,這個實現可以限制工作隊列中任務的數目,以防止隊列中的任務耗盡所有可用內存,另外還提供了多種可用的關閉和飽和度策略(阻塞、廢棄、拋出、廢棄最老的、在調用者中運行等)。所有的 Executor 實現為您治理線程的創建和銷毀,包括當關閉 executor 時,關閉所有線程,另外還為線程創建過程提供了 hook,以便應用程序可以治理它希望治理的線程實例化。例如,這使您可以將所有工作線程放在特定的 ThreadGroup 中,或者賦予它們描述姓名稱。
  
  FutureResult
  
    有時您希望異步地啟動一個進程,同時希望在以后需要這個進程時,可以使用該進程的結果。FutureResult 實用程序類使這變得很輕易。FutureResult 表示可能要花一段時間執行的任務,并且可以在另一個線程中執行此任務,FutureResult 對象可用作執行進程的句柄。通過它,您可以查明該任務是否已經完成,可以等待任務完

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阳朔县| 都江堰市| 西和县| 乌拉特前旗| 北票市| 砀山县| 顺昌县| 海丰县| 定日县| 大连市| 霍林郭勒市| 新绛县| 成武县| 葫芦岛市| 太仓市| 鄂伦春自治旗| 东兰县| 渑池县| 邳州市| 梁山县| 宁陕县| 沂南县| 新津县| 娄底市| 乌鲁木齐县| 海盐县| 无锡市| 仙桃市| 大竹县| 阿瓦提县| 丹东市| 克拉玛依市| 东乌珠穆沁旗| 阜南县| 贡觉县| 柘城县| 湘潭县| 景洪市| 昌平区| 新河县| 湖北省|