什麼 是 多線程呢 ?html
舉個 例子:java
若是要計算很大的 Fibonacci 數,是不能使用該算法的,由於其中有大量的重複計算。圖 27.1 展現了在計算 F6 時所建立的遞歸過程實例樹。其中,對於對於 FIB(6) 的調用會遞歸地調用 FIB(5) 和 FIB(4) 。而對 FIB(5)的調用又會去調用 FIB(4) 。這兩個 FIB(4) 實例返回的結果徹底相同( F4 =3 )。因爲 FIB 並無去記住這些結果,所以對於 FIB(4) 的第二次調用重複了第一次調用的工做。算法
把多線程計算(由一個表明多線程程序的處理器執行的運行時指令集)看作是一個有向無環圖 G= ( V, E )是頗有幫助的,咱們稱其爲計算 dag ( directed acyclic graph ) 。圖 27.2多線程
中給出了一個示例,其中的計算 dag 來自計算 P-FIB(4) 。從概念層面來說, V 中的頂點都是指令, E 中邊則表示指令間的依賴關係, (u,v ) ∈ E 表示指令 u 必須在指令 v 以前執行。爲了方便起見,若是一條指令鏈中不包含任何並行控制語句(沒有 spawn 、 sync 以及被 spawn 例程中 return ——顯式的 return 語句或者過程執行完後的隱式 return ),咱們就把它們組成一組,造成一個 strand ,每一個 strand 都表示一條或者多條指令。涉及並行控制的指令不包括在 strand 中,可是會出如今 dag 結構中。例如,若是一個 strand 有兩個後繼,那麼其中之一必須得被spawn 出來,若是一個 strand 有多個前驅,就表示前驅由於一條 sync 語句被合併在一塊兒。所以,通常來講, V 造成了 strand 集合,而有向邊集合 E 則表示由並行控制產生的 strand 間的依賴關係。若是 G 中有一個從 strand u 到strand v 的有向路徑,那麼咱們就說這兩個 strands 是(邏輯上)串行的。不然就稱其爲(邏輯上)並行的。併發
先來兩張圖:工具
各類狀態一目瞭然,值得一提的是"blocked"這個狀態:
線程在Running的過程當中可能會遇到阻塞(Blocked)狀況post
此外,在runnable狀態的線程是處於被調度的線程,此時的調度順序是不必定的。Thread類中的yield方法可讓一個running狀態的線程轉入runnable。性能
synchronized, wait, notify 是任何對象都具備的同步工具。讓咱們先來了解他們測試
他們是應用於同步問題的人工線程調度工具。講其本質,首先就要明確monitor的概念,Java中的每一個對象都有一個監視器,來監測併發代碼的重入。在非多線程編碼時該監視器不發揮做用,反之若是在synchronized 範圍內,監視器發揮做用。優化
wait/notify必須存在於synchronized塊中。而且,這三個關鍵字針對的是同一個監視器(某對象的監視器)。這意味着wait以後,其餘線程能夠進入同步塊執行。
當某代碼並不持有監視器的使用權時(如圖中5的狀態,即脫離同步塊)去wait或notify,會拋出java.lang.IllegalMonitorStateException。也包括在synchronized塊中去調用另外一個對象的wait/notify,由於不一樣對象的監視器不一樣,一樣會拋出此異常。
咱們以一個真實的故事來結束本小節,該故事發生在世界級多線程國際象棋對弈程序 Socrates[81] 的開發期間(時間作了簡略)。該程序的原型是在一個具備 32 個處理器的計算機上進行的,可是最終運行在一個具備 512 個處理器的超級計算機上。某天,開發人員對程序進行了一項優化,針對一項重要的在 32 個處理器上基準測試( benchmark ),把其運行時間從 T32 = 65 秒減小到 T’32 =40 秒。然而,開發人員使用關於 work和 span 的性能度量得出結論:在 32 個處理器上更快的優化版本,在 512 個處理器上運行時,實際上比原始版本慢一些。結果,他們放棄了「優化」。
他們的分析方法是這樣的。程序的原始版本的 work T1 = 2048 秒, span T∞ = 1 秒。若是把不等式 (27.4)看作是等式, TP = T1 /P+T∞ ,並把它看成在 P 個處理器上的近似運行時間,確實能夠得出, T32=2048/32+1 = 65 。若是作了優化, work 變成 T’1 =1024 秒, span 變成 T’∞ = 8 秒。採用一樣的近似方法,有 T’32 =1024/32 + 8=40 。
可是,當在 512 個處理器上進行計算運行時間時,這兩個版本的相對速度會發生轉換。此時, T512=2048/512+1 = 5 秒, T’512 =1024/512+8 = 10 秒。在 32 個處理器上可以提速程序的優化版本在 512 個處理器上會使得程序慢 2 倍!優化版本的 span 爲 8 ,對於 32 個處理器來講,其不是運行時間中的支配項,可是在 512 個處理器時,就變成了支配項,把更多核的優點化爲烏有。
這個故事的寓意在於, work 和 span 是一種好的推斷性能的方法,而不是一種好的推斷運行時間的方法。