併發編程是計算機學科重要的命題。 如何提綱挈領的掌握併發編程,搭建知識體系尤爲重要。 這篇文章基於本身對於併發編程的理解和公開資料的整理,試圖撥開迷霧,從總體上介紹併發編程。java
主要內容包括:編程
J.U.C框架緩存
併發編程是伴隨計算機發展產生的。安全
第一代計算機使用笨重的卡片機, 工做人員將計算任務打孔的方式輸入到計算機中。
爲了提高運算速度,出現了批處理的操做系統。然後出現了分時操做操做系統。網絡
英特爾(Intel)創始人之一戈登·摩爾提出:集成電路上可容納的晶體管數目,約每隔兩年便會增長一倍。這就是著名的摩爾定律。半導體行業大體按照摩爾定律發展了半個多世紀,對二十世紀後半葉的世界經濟增加作出了貢獻。多線程
CPU從原來的單核逐漸增長成雙核、四核甚至更多。 CPU變的愈來愈快, 內存和IO的速度的增加卻很緩慢。緩存是爲了緩解CPU和內存速度之間的速度不匹配, 內存緩解了CPU和IO設備之間的速度不匹配。架構
如今計算機存儲結構以下圖所示:併發
多線程和多進程的產生就是爲了充分利用多核CPU的計算優點, 減小IO阻塞而產生的。框架
多線程程序有不少優勢:異步
以前的操做系統只能新建少許的線程,所以,操做系統提供了高效的IO方式。好比多路複用IO,異步IO等。現代的操做系統,線程數量限制一般很大, 可使用線程池技術提高處理能力。
傳統的GUI系統不少都是單線程, 經過主事件循環(Main Event Loop)處理界面組件各類事件。在處理比較耗時的事件操做時, 很容易卡住主界面。
如今的GUI系統利用多線程的技術, 使用事件分發機制(Event Dispatch Thread)代替主事件循環。 事件發生時調用對應的事件處理器。 界面系統影響更加靈敏。
一個線程處理一個事情比一次處理多個事情建模更加容易。 經過多線程的框架能夠把事件處理和資源調度、交替執行的操做、異步IO和資源等待隔離開來。如今的併發編程的框架和組件(Servlet,RMI等)使得建模更加簡單。
多線程是一把雙刃劍、帶來性能提高的同時帶來安全性、活性和性能問題。
以酒店清潔爲例子:
昆泰酒店有不少的房間須要打掃、保潔在主管的監督完成全部酒店房間的清潔工做。
公司資金有限, 只有有限的清潔工具供保潔輪流使用。
這個例子中:清掃房間就是接下來計算機要處理的計算任務。保潔至關於處理任務的線程,主管至關於線程調度器,有限的清潔工具至關於CPU資源或者其餘公共資源。
那問題來了,酒店規模很小的時候,整個昆泰酒店只有1名保潔,這名保潔只須要逐個去打掃。處理速度雖然慢,可是不會出錯。清潔工具能夠被這名保潔獨佔,只有一名保潔,所以也不須要額外招聘保潔主管。
隨着酒店規模變大,1名保潔不能知足酒店的要求。公司僱傭更多的保潔,併爲這些保潔人員配置了保潔主管。
多名保潔打掃完房間以後會通知保潔主管該房間已經被打掃。清潔工具在使用完成,交給下一位保潔以前須要清掃乾淨並標記上本身的工號表示本身在使用(上下文切換)。
保潔之間協同工做和多線程系統很像。
保潔打掃一個房間,若是不作任何標記或者通知,別的保潔也可能進入打掃。形成了資源浪費(任務重疊執行)。更不幸的事情,新入住的房客在這個時候進入了房間看到房間一片狼藉,該做何想。
這種狀況至關於公共資源訪問時的線程安全性問題。 線程安全性問題有時候只是形成資源浪費,跟多的時候會形成嚴重的錯誤。
保潔主管發現了這個問題以後,他讓保潔在進入房間門打掃以前在門口放置一個打掃中的牌子(鎖)。另一個保潔看到這個牌子(鎖失效),就知道房間在打掃中。這個問題也就解決了。
房間的打掃狀態至關於Java語言中的鎖。 鎖不緊是能解決互斥訪問,也間接實現了線程之間的通訊。
保潔主管爲了有限的工具(資源)被高效利用設計了一套資源分配的規則(JMM Java內存模型)。 規則規定了不一樣工具放置的位置(內存分佈)、工具使用規則(保證工具使用狀態每一個人均可見)、工序優化的規則(重排序)、工做分配的規則(原子性)。
主管指定的資源分配的規則,至關於Java的內存模型。
Java內存模型包括Java內存區域劃分和內存使用規則(可見性、原子性和指令重排序)。
因爲線程資源稀缺性或者程序自身的問題和缺陷致使線程線程一直處於非runnable狀態,或者線程雖然處於runnable狀態可是其要執行的任務卻一直沒法進展的現象被稱爲線程活性故障。
常見的線程活性故障包括死鎖、活鎖、飢餓。
死鎖:
保潔A手裏拿着拖把, 她須要抹布, 另一個保潔手裏拿着抹布須要拖把。若是兩我的都互不相互謙讓的會就會形成死鎖問題。
形成死鎖須要四個條件: 互斥條件, 請求和保持條件, 不可剝奪和循環等待。 破壞四個條件任何一個,就能夠解決死鎖的問題。
活鎖:並未產生線程阻塞,可是因爲某種問題的存在,致使沒法繼續執行的狀況。
這種時候通常是將不可修復的錯誤不要重試,或者是重試次數限定
好比兩個頗有禮貌的人在同一條路上相遇,彼此給對方讓路,可是又在同一條路上遇到了。互相之間反覆的避讓下去
這種時候能夠選擇一個隨機退讓,使得具有必定的隨機性
優先級低的線程因爲不停的被高優先級線程搶佔執行,致使最終沒法執行。
引入公平機制能夠解決飢餓問題。 好比使用ReentrantLock實現的公平鎖。
併發編程存在因爲互斥資源爭用致使程序吞吐降低,執行變慢等性能問題。
加鎖能夠解決線程資源爭用的問題,但同時帶來開銷。
在 JDK1.6 以後,出現了各類鎖優化技術,如輕量級鎖、偏向鎖、適應性自旋、鎖粗化、鎖消除等
synchronized關鍵字修飾的鎖對象會根據互斥資源爭用狀況選擇不一樣的鎖的實現。
當訪問共享數據時,一般是要使用同步。若是要避免使用同步,就是不提供共享數據。若是僅在單線程中訪問數據,就不須要同步,這種技術就叫作線程封閉。
實現線程封閉主要有三種方式:
同時JDK也提供了AtomicXXX使用CAS方式實現線程同步。
J.U.C是指java.util.concurrent包下的併發類。
J.U.C包裏的類有部分:
線程安全相關的類包括以下三類:
類圖以下所示:
爲了提高房間清掃和人員使用的效率,主管提議使用動態人員的方式配置保潔(線程池)。 酒店天天至少有2名保潔值班(coreSize), 客人離店以後派遣2名保潔打掃。爲了提高服務體驗,若是有超過2個以上房客離店,主管將會從其餘分部調派保潔。不幸的是總部規定每一個酒店最多隻能有10個保潔(maxSize)。若是沒有可用的房間,新來要入店的房客只能等待(Queue)。酒店資源有限,等待的隊列不能無限的長(有界隊列),不然有可能把酒店擠爆,哈哈哈,這種狀況酒店就賺發了。在等待隊列也滿的時候, 須要合理策略(拒絕策略)處理。酒店前臺可選的策略能夠有:
拒絕接待新客人(DiscardPolicy), 不接待新客人通知總部(AbortPolicy,引起RejectExecutionException異常), 讓客人本身打掃房間(CallerRunsPolicy), 隊尾位置讓給新來的客人(DiscardOldestPolicy)。
這就是線程池的模型。線程池模型核心關注:3個容量大小(coreSize, maxSize, queueSize)和4種拒絕策略(DiscardPolicy,AbortPolicy,CallerRunsPolicy,DiscardOldPolicy)。總部會根據酒店客流量狀況安排資源池配置參數(設置coreSize,maxSize和QueueSize),這便是如何設置線程池參數的問題。
在默認ThreadPoolExecutor.AbortPolicy ,處理程序會引起運行RejectedExecutionException後排斥反應。
在ThreadPoolExecutor.CallerRunsPolicy中,調用execute自己的線程運行任務。 這提供了一個簡單的反饋控制機制,將下降新任務提交的速度。
在ThreadPoolExecutor.DiscardPolicy中 ,簡單地刪除沒法執行的任務。
在ThreadPoolExecutor.DiscardOldestPolicy中
須要打掃的房間很少的時候, ExcutorSevice的模式運行效率很好。中午12點以後,大量空閒房間須要打掃。 若是還按照線程池的模式進行任務分配, 保潔人員剛打掃完4層的房間,保潔主管打電話過來講下一步打掃1樓的房間。保潔主管發現這種模式在須要打掃房間比較多的時間(任務比較多)時效率並不高。
爲了解決這個難題, 保潔主管想到了一個辦法。給每一個保潔分配一層樓(一個線程一個工做隊列),有樓層退房比較多, 有的比較少,爲了解決公平和效率的問題。保潔完成本層任務後,隨機去其餘樓層幫忙(work stealing)。
保潔主管想到的這個方法就是Fork/Join模型。 Fork/Join相似ExecutorService
又有不少不一樣的地方。 Fork/Join能夠把大任務(酒店),拆分紅小任務(房間/樓層)。
Fork/Join模型和ExecutorService模型最主要兩點區別是
(1) 一個線程一個工做隊列:每一個人負責一個樓層。
(2) 工做竊取:忙完本身的樓層,隨機去其餘樓層幫忙。
多線程編程是實現併發編程主要途徑。 經過線程可以大大提高系統吞吐和性能。可是,對於多線程編程要解決兩個核心問題:
咱們可使用等待/通知,共享變量,管道等方式進行線程通訊,好比Object的wait/notify, Condition的await/notify, LockSupport的park/unpark, volatile,InheritableThreadLocal, PipeInputStream/PipeOutputStream等。
線程同步咱們能夠加鎖的技術(synchronized,Lock),也可使用無鎖技術(CAS)。
因爲篇幅的緣由。關於線程通訊和線程同步的問題,會單獨寫一篇文章去講解。
回覆「資料」,免費獲取 一份獨家嘔心整理的技術資料!