synchronized 關(guān)鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變量的訪問:每個類實例對應(yīng)一把鎖,每個 synchronized 方法都必須獲得調(diào)用該方法的類實例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執(zhí)行狀態(tài)。這種機制確保了同一時刻對于每一個類實例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個處于可執(zhí)行狀態(tài)(因為至多只有一個能夠獲得該類實例對應(yīng)的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實例,每一個類也對應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為 synchronized ,以控制其對類的靜態(tài)成員變量的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由于在線程的整個生命期內(nèi)它一直在運行,因此將導(dǎo)致它對本類任何 synchronized 方法的調(diào)用都永遠不會成功。當(dāng)然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,并在主方法中調(diào)用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關(guān)鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject)
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執(zhí)行,具體機制同前所述。由于可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
notify()及notifyAll()是Object的方法,與Object的wait()方法配合使用,而且這三個方法必須在同步塊中調(diào)用.
如下:
在線程1中執(zhí)行如下代碼
...
synchronized(obj1) //1.進入同步塊
{
try {
...
obj1.wait(); //2.進入暫停狀態(tài)
}catch (InterruptedException exp) {}
}
1.當(dāng)前同步對象(monitor)為obj1,obj1是任一實例,若是同步方法,則同步對象是this.進入同步塊后,obj1為鎖定狀態(tài),鎖定狀態(tài)對obj1本身無任何影響,而其它線程執(zhí)行到同一代碼時,因不能獲得鎖,處于Block狀態(tài),一旦解鎖,被block的線程自動繼續(xù)執(zhí)行.
2.調(diào)用obj1.wait()有兩個作用,一是使線程進入等待狀態(tài),二是解鎖obj1,這時被block的線程將獲得鎖.線程1要退出等待必須要由其它線程顯式的調(diào)用obj1.notify()或notifyAll()方法.
如
...
synchronized(obj1)
{
...
obj1.notify(); //3. 向wait的線程發(fā)通知信息
...
}
...
若其它線程執(zhí)行到此時,線程1處于wait狀態(tài),則wait狀態(tài)解除,解除后,若線程1若得到鎖就能繼續(xù)執(zhí)行,若有多個線程處于obj1的wait狀態(tài),notify將隨機選一個線程激活,而notifyAll是同時解除所有的wait狀態(tài).
notifyAll()讓等待某個對象K的所有線程離開阻塞狀態(tài),
notify()隨機地選取等待某個對象K的線程,讓它離開阻塞狀態(tài)。
notify(),notifyAll()非常有用,在一個synchronized(lockObj)塊中當(dāng)調(diào)用lockObj.wait()時,線程就已經(jīng)將鎖放開來了,這時當(dāng)另外一個線程調(diào)用lockObj.notify()時,等待的線程就會繼續(xù)執(zhí)行下去了。這是一種非常高效的線程同步機制。如果沒有他,用sleep()來同步的話就太浪費時間了。
一個簡單的例子:
thread1 receive data
thread2 pase received data
lockObj是buf
當(dāng)buf中沒有數(shù)據(jù)時,thread2中調(diào)用buf.wait釋放鎖,讓thread1有機會執(zhí)行。
當(dāng)thread1收到數(shù)據(jù)時,調(diào)用buf.notify()通知thread1去處理收到的數(shù)據(jù)。
如果在同步塊入口點阻塞,不須其它線程調(diào)用notify(),調(diào)了也沒效果,同步塊能自動獲得鎖
如果是wait造成了阻塞,須用notfiy()激活,這兩個方法是配合使用
notify、notifyAll、wait :
主要是為了解決持有監(jiān)視器鑰匙的線程暫停等待另一線程完成時可能發(fā)生死鎖的問題。wait()方法使調(diào)用線程等待,直到發(fā)生超時或另一線程調(diào)用同一對象的notify()或notifyAll()方法。wait() 方法的用法如下:wait()或wait(long timeoutPeriodInMilliseconds)前者等待被通知,后者等待超時或通知。線程調(diào)用wait()方法時,釋放所持有的鑰匙讓另一等待線程進入監(jiān)視區(qū)。notify()方法只喚醒一個等待線程,而notifyAll()喚醒所有等待該監(jiān)視器的線程。注意,這些方法只能在監(jiān)視區(qū)內(nèi)調(diào)用。否則會輸出一種RuntimeException類型的IllegaMonitorStateException異常狀態(tài)。
夠詳細(xì)清楚的吧。
總之wait()讓線程等待,notify()和notifyall()激活某個等待線程,其實就是撤銷該線程的中斷狀態(tài),從而使他們有機會再次運行
有時會遇到如下問題,程序的一部分在寫數(shù)據(jù),另一部分讀數(shù)據(jù),但有時會出現(xiàn)讀部分超前寫部分,
這就是典型的產(chǎn)生者/消耗者問題.
.wait:是一個線程睡眠,直到在相同的對象上調(diào)用notify或notifyAll
.notify:啟動第一個在相同對象調(diào)用wait的線程
.notifyAll:啟動在相同的對象調(diào)用wait的所有線程