[溫故]圖解java多線程設計模式(一)


去年看完的《圖解java多線程設計模式》,可惜當時沒做筆記,導致后來忘了許多東西,打算再溫習下這本書,順便在這里記錄一下~

一、順序執行、並行、並發

順序執行:多個操作按照順序依次執行。

並行:多個任務同時進行,同一時間內可以執行多個任務,這種方式,叫做並行執行,比如多核處理器,多個核可以同時處理多個任務。

並發:多個任務通過切分時間段,來達到“同時進行”的效果,比如單核處理器,在“同時”處理多個任務時,就會不停的切換來執行不同的任務,不可能有同一時間執行不同任務的情況。

下面引用別人的一句話來說明下並行和並發:

並發是兩個任務可以在重疊的時間段內啟動,運行和完成。並行是任務在同一時間運行,例如,在多核處理器上。 並發是獨立執行過程的組合,而並行是同時執行(可能相關的)計算。 並發是一次處理很多事情,並行是同時做很多事情。

應用程序可以是並發的,但不是並行的,這意味着它可以同時處理多個任務,但是沒有兩個任務在同一時刻執行。

應用程序可以是並行的,但不是並發的,這意味着它同時處理多核CPU中的任務的多個子任務。

一個應用程序可以即不是並行的,也不是並發的,這意味着它一次一個地處理所有任務。

應用程序可以即是並行的也是並發的,這意味着它同時在多核CPU中同時處理多個任務。

二、synchronized修飾符

當我們說一個線程獲得鎖以后,則意味着這個線程可以執行當前對象(或類)里的synchronized方法,而且他線程則需要排隊等待該線程釋放鎖以后才可能獲得鎖,進而執行鎖里面的程序。

synchronized修飾后,存在對象鎖和類鎖兩種類型。

2.1:對象鎖


synchronized (this){
   ...略
}

2.2:類鎖


synchronized (XXX.class){
   ...略
}

2.3:區別和作用域

對象鎖指的是當前線程獲得了某個實例的鎖,假如有個Word類,有A、B兩個同步方法,C屬於普通方法,如圖所示:

圖1

可以發現,對象鎖的作用域只針對當前對象生效,就像w1和w2里的A方法可以被不同的線程同時執行,但是同一個對象內的同步塊,卻只允許持有當前對象鎖的線程執行,如t2、t3均被擋在了外面,當t1釋放鎖以后,t2、t3才會重新競爭鎖,競爭到鎖以后就會執行自己想要執行的同步邏輯。

類鎖指的是當前線程獲得了某個類的鎖,還是Word類,有A、B兩個static方法(靜態方法屬於類方法,加synchronized修飾符后等效於上面提到的synchronized(Word.class)),C屬於普通static方法,如圖所示:

圖2

跟上面相比較,這里的t5受到了t1的影響,因為t1獲得了Word類的鎖,w1和w2共屬一個類,因此t1獲得類鎖以后,其他線程想要訪問這個類里的同步塊,就得等到t1釋放鎖以后才可以繼續競爭鎖然后執行自己想要執行的同步邏輯。

三、線程間的通信

3.1:Wait

這幾個方法是屬於每個實例對象的,所有實例都擁有一個“等待隊列”(虛擬概念,實例里並不存在該字段),它是在實例的wait方法調用后存放停止操作線程的隊列。執行wait方法后,線程進入當前實例的“等待隊列”,以下幾種情況可以讓線程退出“等待隊列”:

①其他線程調用notify、notifyAll方法來將其喚醒

②其他線程調用interrupt來將其喚醒

③wait方法本身超時

 

當執行了下面的代碼:


obj.wait();

我們可以說當前線程在obj上發生了等待,當前線程進入了obj的“等待隊列”,此時當前線程會讓出鎖,讓其他線程繼續競爭獲得該實例的鎖(因此這里有個規則,調用wait的線程必須持有當前實例對象的鎖

過程如下圖:

圖3

 

3.2:notify

現在先來介紹下notify,該方法會將等待隊列里的線程取出,讓其退出等待並參與鎖競爭然后繼續執行上次wait后沒有執行完的語句。整體過程如下圖所示:

圖4

可以看到,t1在被掛起后,會因為t2調用了同實例的notify方法,而讓t1被從等待隊列里釋放,重新加入到所得競爭力,t2執行完畢后釋放鎖,鎖又再次被t1競爭到,t1將繼續執行上次被掛起時后面未執行完的語句。

需要指出的是,如果等待隊列里的線程是多個,那么被喚醒的那一個,將會是等待隊列里所有線程隨機的一個,不會特定哪一個線程會被喚起。

3.3:notifyAll

接下來介紹notifyAll方法,顧名思義,就是將等待隊列里的線程全部喚起,然后這些線程將全部加入到鎖競爭,競爭到,繼續完成上次被掛起時未執行完畢的操作,流程圖如下:

圖5

 

說明,當線程調用實例的wait、notify、notifyAll方法有個大前提,就是必須要求該線程擁有該實例的鎖,否則會拋IllegalMonitorStateException異常。

在編寫程序時,是該選擇notify還是選擇notifyAll?這個可以指出的是,notifyAll往往更加健壯,而notify由於喚起的線程少,因此效率會更高,但是存在程序停止的風險。

附上使用wait、notify進行線程通信的例子:

利用ReentrantLock簡單實現一個阻塞隊列

java設計模式:簡單實現生產者和消費者模式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM