elasticsearch入門系列">elasticsearch入門系列
722
2022-05-30
文章目錄
一、線程并行相關概念
同步(Synchronous)和異步(Asynchronous)
并發(Concurrency)和并行(Parallelism)
臨界區
阻塞(Blocking)和非阻塞(Non-Blocking)
饑餓(Starvation)、死鎖(Deadlock)和活鎖(Livelock)
二、并行的兩個重要定律
Amdahi定律
Gustafson定律
三、多線程的特性
原子性
可見性
有序性
一、線程并行相關概念
同步和異步的本質區別是是否需要等待,比如一個方法在執行,必須等前面一個方法程執行完成,才可以執行,這就是同步。如果不需要等上一個方法執行完成,并行或者并發執行,這就是異步調用。
并發和并行兩個概念很容易混淆。解釋起來意思也差不多,不過說起來,并行才是真正意義上的并行執行,并發只是線程的交替執行,有可能存在串行的情況。
在單核CPU的系統,線程只能是并發的,而不能支持并行,并行執行只能存在與多核CPU的系統。
臨界區,可以理解為公共的資源或者說共享數據。臨界區具有保護性,也就是說,只能一個線程占用臨界區,一旦一個線程占了臨界區,另外一個線程是不予許再占用的,必須等線程釋放了才行。
阻塞是線程的一種比較嚴重的情況,從前面我們知道了臨界區只能允許一個線程占用,假如一個線程因為執行時間過長,占用了臨界區,不掛起,其它想要占用臨界區的線程只能等待,這種情況就容易造成線程阻塞。非阻塞的話就相反了,指所有線程都正常執行,不會出現線程占臨界區不掛起的情況。
饑餓,有些情況可能是一個線程優先級太低了,每次都被其它線程占用了,導致改線程一種不能占用臨界區。也有一些情況是上一個線程執行時間太長了,一直沒釋放,導致其它線程都不能占用臨界區,這也是造成線程饑餓。
死鎖有可能是因為線程死循環調用等等情況造成的,一旦出現這種情況估計就得人工排查了。
活鎖,解釋一下,一般就是這樣的情況,因為線程互相掛起臨界區,給其它線程用,互相“謙讓”,導致資源在兩個或者幾個線程之間跳到,這種情況就是活鎖。
二、并行的兩個重要定律
Amdahi定律定義了串行系統并行化后的加速比公式。
加速比定義:加速比 = 優化前系統耗時 / 優化后系統耗時
加速比越高,說明優化越明顯。簡單介紹一下Amdahi定律公式的推導。
優化后耗時T_n=T1(F+1/n(1-F)),其中T1表示優化前耗時,F表示串行比例,(1-F)表示并行比例,下標n就是處理器的個數。
導入加速比公式,也就是T1/T_n,也就是1/(F + 1/n(1-F)),公式只是進行簡單介紹。
從公式可以看出,加速比是和串行比例F成反比的,從公式可以看出增加cpu的個數僅僅是一種提供加速比的方法,增加cpu個數的同時,還可以提供降低串行比例來做,也就是串行比例F越低,加速比也就越高
Custafson公式也是并行的一個比較重要的公式,現在介紹一下Custafson公式的推導。
定義一下串行執行時間為a,并行執行時間為b。即單核CPU情況,執行時間為a+b總執行時間為a+nb,n表示CPU個數。
//定義串行比例
F=a/(a+b)
//得到加速比
s(n)=a+nb/a+b=a/a+b + nb/a+b = F + n*(b-a+a)/a+b = F + n(1-F)
從公式可以看出,如果串行比例足夠小的情況,加速比其實就是約等于處理器個數,也就是說通過加多CPU的個數就能提高加速比。
兩個公式看起來似乎有點矛盾,其實不然,兩個公式只是從不同角度分析問題。Amdahi是說在串行比例一定時,通過加CPU的方法是有上限的,通過降低串行比例同時增加cpu個數可以提高加速比。Custafson是說在串行比較趨于很小的情況,從公式可以看出,加cpu就可以提高加速比
三、多線程的特性
因為多線程環境的數據不一致性和安全性,所以就需要一些規則類控制,Java的內存模型JMM就規范了多線程有效正確的執行,而JMM也正是圍繞多線程的原子性、可見性、有序性進行的,所以本博客介紹一些多線程的原子性、可見性和有序性
對于單線程來說,確實是具有原子性的,比如一個int變量,改變一下值,去讀取的時候是那個值,這是很正常的,我們去系統運行,也是這樣的,因為我們的操作系統大部分是32位和64位的,int類型4個字節,也就是32位,不過可以試試long類型的數值,long類型是8個字節,也就是64位,如果兩個線程都對其進行讀寫呢?在多線程環境,一個線程改變了long類型的值,然后再去讀取,獲取到的值就不一定是剛才改變的值了,因為你的系統可能是32位的,而long類型是64位的,如果兩個線程都對long類型進行讀寫,就會出現這種情況。
對于可見性又應該怎么理解?首先說一下對于單線程來說,是并不存在可見性的,可見性是針對多線程來說的,比如,一個線程進行了改變,另外一個線程是否知道這個線程做了改變,這個就是可見性。舉個例子,變量a是共享變量,一個cpu1上的變量a做了緩存優化,將變量a放在了緩存里,這時,另外一個cpu2上線程對變量a做了改變,這個操作對于cpu1上的線程是不可見的,因為cpu1已經做了緩存,所以cpu1上的線程就從緩存讀取變量a了,發現和cpu2上讀取的值并不一致。
對于單線程來說,一個線程的代碼執行是按照先后順序的,這樣說是沒錯的,但是在多線程環境可不一定了。因為在多線程環境可能發生指令的重排。也就是說多線程環境,代碼執行是不一定具有有序性的。
既然無序性是重排導致的,那么是所有的指令都會重排的?當然不是。重排按照:Happen-Before規則。
列舉一下:
引用葛一鳴/郭超/. 實戰Java高并發程序設計 (597-601).
程 序 順 序 原 則: 一 個 線 程 內 保 證 語 義 的 串 行
性 volatile 規 則: volatile 變 量 的 寫, 先 發 生 于 讀, 這 保 證 了 volatile 變 量 的 可 見 性
鎖 規 則: 解 鎖( unlock) 必 然 發 生 在 隨 后 的 加 鎖( lock) 前
傳 遞 性: A 先 于 B, B 先 于 C, 那 么 A 必 然 先 于 C
線 程 的 start() 方 法 先 于 它 的 每 一 個 動 作
線 程 的 所 有 操 作 先 于 線 程 的 終 結( Thread.join())
線 程 的 中 斷( interrupt()) 先 于 被 中 斷 線 程 的 代 碼
對 象 的 構 造 函 數 執 行、 結 束 先 于 finalize() 方 法
任務調度 多線程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。