1. 程式人生 > >java多執行緒同步以及執行緒間通訊詳解&消費者生產者模式&死鎖&Thread.join()(多執行緒程式設計之二)

java多執行緒同步以及執行緒間通訊詳解&消費者生產者模式&死鎖&Thread.join()(多執行緒程式設計之二)



從執行結果,我們就可以看出我們4個售票視窗同時賣出了1號票,這顯然是不合邏輯的,其實這個問題就是我們前面所說的執行緒同步問題。不同的執行緒都對同一個資料進了操作這就容易導致資料錯亂的問題,也就是執行緒不同步。那麼這個問題該怎麼解決呢?在給出解決思路之前我們先來分析一下這個問題是怎麼產生的?我們宣告一個執行緒類Ticket,在這個類中我們又聲明瞭一個成員變數num也就是票的數量,然後我們通過run方法不斷的去獲取票數並輸出,最後我們在外部類TicketDemo中建立了四個執行緒同時操作這個資料,執行後就出現我們剛才所說的執行緒同步問題,從這裡我們可以看出產生執行緒同步(執行緒安全)問題的條件有兩個:1.多個執行緒在操作共享的資料(num),2.操作共享資料的執行緒程式碼有多條(4條執行緒);既然原因知道了,那該怎麼解決?解決思路:
將多條操作共享資料的執行緒程式碼封裝起來,當有執行緒在執行這些程式碼的時候,其他執行緒時不可以參與運算的。必須要當前執行緒把這些程式碼都執行完畢後,其他執行緒才可以參與運算。 好了,思路知道了,我們就用java程式碼的方式來解決這個問題。2.解決執行緒同步的兩種典型方案在java中有兩種機制可以防止執行緒安全的發生,Java語言提供了一個synchronized關鍵字來解決這問題,同時在Java SE5.0引入了Lock鎖物件的相關類,接下來我們分別介紹這兩種方法2.1通過鎖(Lock)物件的方式解決執行緒安全問題在給出解決程式碼前我們先來介紹一個知識點:Lock,鎖物件。在java中鎖是用來控制多個執行緒訪問共享資源的方式,一般來說,一個鎖能夠防止多個執行緒同時訪問共享資源(但有的鎖可以允許多個執行緒併發訪問共享資源,比如讀寫鎖,後面我們會分析)。在Lock接口出現之前,java程式是靠synchronized關鍵字(後面分析)實現鎖功能的,而JAVA SE5.0之後併發包中新增了Lock介面用來實現鎖的功能,它提供了與synchronized關鍵字類似的同步功能,只是在使用時需要顯式地獲取和釋放鎖,缺點就是缺少像
synchronized那樣隱式獲取釋放鎖的便捷性,但是卻擁有了鎖獲取與釋放的可操作性,可中斷的獲取鎖以及超時獲取鎖等多種synchronized關鍵字所不具備的同步特性。接下來我們就來介紹Lock介面的主要API方便我們學習
方法相關描述內容                                                                                                                                                                                                                                                                                                                  
void lock()獲取鎖,呼叫該方法當前執行緒會獲取鎖,當獲取鎖後。從該方法返回
void lockInterruptibly()
throws InterruptedException
可中斷獲取鎖和lock()方法不同的是該方法會響應中斷,即在獲取鎖
中可以中斷當前執行緒。例如某個執行緒在等待一個鎖的控制權的這段時
間需要中斷。
boolean tryLock()嘗試非阻塞獲取鎖,呼叫該方法後立即返回,如果能夠獲取鎖則返回
true,否則返回false。
boolean tryLock(long time,TimeUnit unit)
throws  InterruptedException
超時獲取鎖,當前執行緒在以下3種情況返回:
1.當前執行緒在超時時間內獲取了鎖
2.當前執行緒在超時時間被中斷
3.當前執行緒超時時間結束,返回false
void unlock()釋放鎖
Condition newCondition()條件物件,獲取等待通知元件。該元件和當前的鎖繫結,當前執行緒只有
獲取了鎖,才能呼叫該元件的await()方法,而呼叫後,當前執行緒將縮放
鎖。
這裡先介紹一下API,後面我們將結合Lock介面的實現子類ReentrantLock使用某些方法。ReentrantLock(重入鎖):
重入鎖,顧名思義就是支援重新進入的鎖,它表示該鎖能夠支援一個執行緒對資源的重複加鎖,也就是說在呼叫lock()方法時,已經獲取到鎖的執行緒,能夠再次呼叫lock()方法獲取鎖而不被阻塞,同時還支援獲取鎖的公平性和非公平性。這裡的公平是在絕對時間上,先對鎖進行獲取的請求一定先被滿足,那麼這個鎖是公平鎖,反之,是不公平的。那麼該如何使用呢?看範例程式碼:
1.同步執行的程式碼跟synchronized類似功能:
ReentrantLock lock = new ReentrantLock(); //引數預設false,不公平鎖  
ReentrantLock lock = new ReentrantLock(true); //公平鎖  
  
lock.lock(); //如果被其它資源鎖定,會在此等待鎖釋放,達到暫停的效果  
try {  
    //操作  
} finally {  
    lock.unlock();  //釋放鎖
}  
2.防止重複執行程式碼: