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