第一課-並行編程的幾個概念

 爲什麼需要並行?
1. 業務要求

    爲了讓用戶有幹好的產品使用感受,如ajax的異步請求,通過異步的執行讓程序給用戶帶來的體驗更加完美
2. 性能

    例如一個分析數據的任務一個線程需要20秒鐘,10個線程並行處理可能3秒鐘就可以分析完成,爲了更快的響應的速度更好的性能我們使用並行

3.摩爾定律的失效

預計18個月會將芯片的性能提高一倍
Intel CEO Barret單膝下跪對取消4GHz感到抱歉
在2004年秋季,Intel宣佈徹底取消4GHz計劃
雖然現在已經有了4GHZ的芯片,但頻率極限已經逼近
10年過去了,我們還停留在4GHZ

頂級計算機科學家唐納德·爾文·克努斯說過:在我看來,這種現象(併發)或多或少是由於硬件設計者已經無計可施了導致的,他們將摩爾定律失效的責任


並行編程的幾個重要的概念

同步(synchronous)和異步(asynchronous)

同步,可以理解爲在執行完一個函數或方法之後,一直等待系統返回值或消息,這時程序是出於阻塞的,只有接收到返回的值或消息後才往下執行其他的命令。  

異步,執行完函數或方法後,不必阻塞性地等待返回值或消息,只需要向系統委託一個異步過程,那麼當系統接收到返回值或消息時,系統會自動觸發委託的異步過程,從而完成一個完整的流程。

 併發(Concurrency)和並行(Parallelism)

併發和並行都可以是很多個線程,就看這些線程能不能同時被多個cpu執行,如果可以就是並行,而併發是多個線程用一個cpu輪流切換着執行


 臨界區

臨界區用來表示一種公共資源或者說是共享數據,可以被多個線程使用。但是每一次,只能有一個線程
使用它,一旦臨界區資源被佔用,其他線程要想使用這個資源,就必須等待。


 阻塞(Blocking)和非阻塞(Non-Blocking)

  阻塞和非阻塞通常用來形容多線程間的相互影響。比如一個線程佔用了臨界區資源,那麼其它所有需要
這個資源的線程就必須在這個臨界區中進行等待,等待會導致線程掛起。這種情況就是阻塞。此時,如
果佔用資源的線程一直不願意釋放資源,那麼其它所有阻塞在這個臨界區上的線程都不能工作。
 非阻塞允許多個線程同時進入臨界區


死 鎖(Deadlock)、飢餓(Starvation)和活鎖(Livelock)

死 鎖

是指兩個或兩個以上的進程(或線程)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。

產生死鎖的條件:

互斥條件:

線程對資源的訪問是排他性的,如果一個線程對佔用了某資源,那麼其他線程必須處於等待狀態,直到資源被釋放。

請求和保持條件:

線程T1至少已經保持了一個資源R1佔用,但又提出對另一個資源R2請求,而此時,資源R2被其他線程T2佔用,於是該線程T1也必須等待,但又對自己保持的資源R1不釋放。

不剝奪條件:

線程已獲得的資源,在未使用完之前,不能被其他線程剝奪,只能在使用完以後由自己釋放。

環路等待條件:

在死鎖發生時,必然存在一個“進程-資源環形鏈”,即:{p0,p1,p2,...pn},進程p0(或線程)等待p1佔用的資源,p1等待p2佔用的資源,pn等待p0佔用的資源。(最直觀的理解是,p0等待p1佔用的資源,而p1而在等待p0佔用的資源,於是兩個進程就相互等待)

飢餓

 是指如果線程T1佔用了資源R,線程T2又請求封鎖R,於是T2等待。T3也請求資源R,當T1釋放了R上的封鎖後,系統首先批准了T3的請求,T2仍然等待。然後T4又請求封鎖R,當T3釋放了R上的封鎖之後,系統又批准了T4的請求......,T2可能永遠等待。

活鎖

電梯遇人,是指線程1可以使用資源,但它很禮貌,讓其他線程先使用資源,線程2也可以使用資源,但它很紳士,也讓其他線程先使用資源。這樣你讓我,我讓你,最後兩個線程都無法使用資源。

 

 併發級別


 阻塞

     當一個線程進入臨界區後,其他線程必須等待無障礙

無障礙是一種最弱的非阻塞調度
 自由出入臨界區
 無競爭時,有限步內完成操作
  有競爭時,回滾數據
 無鎖

是無障礙的
保證有一個線程可以勝出


 無等待

無鎖的
要求所有的線程都必須在有限步內完成

並行的2個重要定律

阿姆達爾定律 Amdahl
加速比定義:加速比 = 優化前系統耗時/優化後系統耗時
Tn = T1(F+1/n(1-F))
F串行比例 1-F 並行比例
n處理器個數
加速比 = T1/Tn 優化前耗時/優化後耗時
1/(F+1/n(1-F))

加速比=優化前系統耗時/優化後系統耗時=500/400=1.25

結論:增加CPU數量並不一定能起到有效的作用,提高系統內可並行的模塊比重,合理增加並行處理器數量,才能以最小的投入,得到最大的加速比

古斯塔夫森定律 Gustafson
執行時間 :a + b a 串行時間 b並行時間
總執行時間 a+nb n處理器個數
加速比 (a+nb)/(a+b)
定義 F=a/(a+b)串行比例
加速比:n-F(n-1)

結論:只要有足夠的並行化,那麼加速比和CPU個數成正比

JMM的一些概念

1.原子性,指一個操作是不會中斷的並且不會受別的線程干擾,並且要麼成功要麼失敗,原子性體現最多的是數據庫的事務,java中也會有類似的問題,例如在32位的java虛擬機中long類型是64位的如果多個線程同時併發去寫一個long類型就會出現一個線程的操作被另外的線程覆蓋,這說明操作是非原子的

2.可見性,在串行操作系統中,由於cpu指令是串行運行的,不會出現可見性問題,在多核計算器中,由於某些變量是共享的,所以就會出現多個cpu同時改了某個共享變量但這個變量副本沒有及時同步到主存中,造成數據可見性存在問題,也就是一個線程去觀察另外一個線程的變量,看到的值是不準確的

3.有序性,cpu指令執行的時候爲什麼會出現順序和我們看到的代碼不一致了,原因是cpu指令進行了優化,運用了統籌學的方法,中間消除等待,提高效率,cpu會選擇最省時的指令運行順序,所以多線程中cpu指令執行順序和代碼的順序是不一致的

4.先行發生的幾個原則

  • 程序次序法則:線程中的每個動作A都happens-before於該線程中的每一個動作B,其中,在程序中,所有的動作B都能出現在A之後。
  • 監視器鎖法則:對一個監視器鎖的解鎖 happens-before於每一個後續對同一監視器鎖的加鎖。
  • volatile變量法則:對volatile域的寫入操作happens-before於每一個後續對同一個域的讀寫操作。
  • 線程啓動法則:在一個線程裏,對Thread.start的調用會happens-before於每個啓動線程的動作。
  • 線程終結法則:線程中的任何動作都happens-before於其他線程檢測到這個線程已經終結、或者從Thread.join調用中成功返回,或Thread.isAlive返回false。
  • 中斷法則:一個線程調用另一個線程的interrupt happens-before於被中斷的線程發現中斷。
  • 終結法則:一個對象的構造函數的結束happens-before於這個對象finalizer的開始。
  • 傳遞性:如果A happens-before於B,且B happens-before於C,則A happens-before於C

以上是總結的java多線程的一些基礎概念,以後的文章會對每種概念進行分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章