併發最多見用於線程池,顯然使用線程池能夠有效的提升吞吐量。html
最多見、比較複雜一個場景是Web容器的線程池。Web容器使用線程池同步或者異步處理HTTP請求,同時這也能夠有效的複用HTTP鏈接,下降資源申請的開銷。一般咱們認爲HTTP請求時很是昂貴的,而且也是比較耗費資源和性能的,因此線程池在這裏就扮演了很是重要的角色。java
在線程池的章節中很是詳細的討論了線程池的原理和使用,同時也提到了,線程池的配置和參數對性能的影響是巨大的。不盡如此,受限於資源(機器的性能、網絡的帶寬等等)、依賴的服務,客戶端的響應速度等,線程池的威力也不會一直增加。達到了線程池的瓶頸後,性能和吞吐量都會大幅度下降。redis
一直增長機器的性能或者增大線程的個數,並不必定能有效的提升吞吐量。高併發的狀況下,機器的負載會大幅提高,這時候機器的穩定性、服務的可靠性都會降低。sql
儘管如此,線程池依然是提升吞吐量的一個有效措施,配合合適的參數可以有效的充分利用資源,提升資源的利用率。apache
除了線程池是比較發雜的併發場景外,任務隊列也是一個不錯的併發工具。JDK內部有大量的隊列(Queue),這些工具不只可以方便使用,提升生產力,也可以進行組合適應於不一樣的場景。即便線程池內部,也是用了任務隊列來處理任務的積壓,平衡資源的消耗。安全
安全的任務隊列可以有效的平衡機器的複雜,抵消因爲峯值和波動帶來的不穩定,有效提升服務的可靠性。同時任務隊列的處理也有助於統計和分析服務的情況。網絡
任務隊列也能夠在多個線程之間傳遞數據,有助於並行處理任務。例如經典的「生產者-消費者」模型就能夠有效的提升多個線程的並行處理能力。在IO延時比較大的服務中尤爲有效。 我最喜歡的一個案例是導數據是,一個線程負責往固定大小的任務隊列中壓入大量的數據,隊列滿了之後就暫停,另外幾個線程負責從任務隊列中獲取數據並消費。這將串行的「生產-消費」,變成了並行的「生產-消費」。實踐證實極大的節省任務處理時間。多線程
線程池也是異步處理的一種表現形式,除此以外,使用異步處理的目的也是爲了提升服務的處理速度。 例如AOP的一個例子就是使用切面來記錄日誌,若是說咱們要遠程收集日誌,顯然不但願因爲收集日誌而影響服務自己。這時候就將日誌收集的過程進行異步處理。併發
現在大量的開源組件都喜歡使用異步處理來提升IO的效率,某些不須要同步返回的操做使用異步處理後可以有效的提升吞吐量。異步
固然,異步也不老是使人滿意的,也會有相應的問題。例如引入異步設計後的複雜性,線程中斷後的處理機制,失敗後的處理策略,產生的消息比消費的還快時怎麼辦,關閉程序時如何關閉異步處理邏輯等等。這都會增長系統的複雜性。
儘管大量的服務、業務使用異步來處理,可是很顯然須要有保障機制可以保證異步處理的邏輯正確性。若是認爲異步處理的任務不是特別重要,或者說主業務不能由於附屬業務的邏輯出錯而崩潰,那麼使用異步處理是正確的選擇。
併發操做的同時還須要維護數據的一致性,或多或少的會涉及到同步操做。正確的使用原子操做,合理的使用獨佔鎖和讀寫鎖也是一個很大的挑戰。
線程間的協調與通訊,尤爲是狀態的同步都是比較困難的。咱們看到線程池ThreadPoolExecutor的實現爲了解決各個線程的執行狀態,引入的不少的同步操做。線程愈來愈多的狀況下,同步的成本會愈來愈高,同時也有可能引入死鎖的狀況。
儘管如此,單個JVM內部的多線程同步仍是比較容易控制的。JDK內部也提供了大量的工具來方便完成數據的同步。例如Lock/Condition/CountDownLatch/CyclicBarrier/Semaphore/Exchanger等等。
分佈式的併發問題更難以處理,根據CAP的原理,基本上沒有一個至善至美的方案。 分佈式資源協調使用分佈式鎖是一個不錯的選擇。Google的分佈式鎖(創建在BigTable之上),Zookeeper的分佈式鎖,甚至簡單的利用memcache的add操做或者redis的setnx操做創建僞分佈式鎖也能夠解決相似的問題。