多線程的同步
多線程並發訪問同一個對象(臨界資源),如果不對線程進行同步控制,破壞了原子操作(不可再分的操作),則會造成臨界資源(兩個線程同時訪問的資源)的數據不一致。
每一個對象都有一個互斥的鎖標記和一個鎖池。當線程擁有這個對象的鎖標記時才能訪問這個資源,沒有鎖標記便進入鎖池,保證在同步代碼塊中只有一個線程,解決了多線程同步控制的問題。
關鍵字:synchronized //線程在同步代碼中必須采用串行訪問
synchronized修飾代碼塊:對括號內的對象object加鎖,只有拿到對象鎖標記的線程才能進入該代碼塊。
public void push(char c){
//object只要是對象就可以,但必須保證是同一對象
synchronized(object){
……
同步代碼
……
}
}
synchronized修飾方法:在整個方法範圍內對當前對象的加鎖,只有拿到對象鎖標記的線程才能執行該方法,盡可能的少用。
public synchronized void push(char c) {
……
同步代碼
……
}
一個線程可以同時擁有多個對象的鎖標記,鎖標記如果過多,就會出現線程等待其他線程釋放鎖標記,而又都不釋放自己的鎖標記供其他線程運行的狀況,造成死鎖。
靜態方法可以是同步方法:但是它所鎖的並不是當前對象,是類對象。
抽象方法不能是synchronized同步的方法。
構造方法不能是synchronized同步的方法。
線程因為未拿到鎖標記而發生阻塞進入鎖池(lock pool)。每個對象都有自己的一個鎖池的空間,用於放置等待運行的線程。由系統決定哪個線程拿到鎖標記並運行。
利用Collections類中的synchronizedXxxx(Xxxx ss)方法可以得到相應集合的線程安全的集合。
- 註意:
在同步語句塊中不能直接操作對象鎖正在使用的對象。
對象與鎖一一對應。
同步依賴對象鎖,鎖對象相同,同步語句串行,鎖對象不同,同步語句並行。
順序鎖,不要回調,反向打開。
能不用同步就不用同步,有數據共享沖突時才使用同步。
等待通知機制:
線程間通信使用的空間稱之為對象的等待對列(wait pool),該隊列也是屬於對象的空間的。
使用Object類中wait()的方法,在運行狀態中,線程調用wait(),此時表示線程將釋放自己所有的鎖標記和CPU的占用,同時進入這個對象的等待池。等待池的狀態也是阻塞狀態,只不過線程釋放自己的鎖標記。只有在對該對象加鎖的同步代碼塊裏,才能掉用該對象的wait(),表示線程將會釋放所有鎖標記,進入等待隊列,線程將進入等待隊列狀態。
一個線程進入了一個對對象加鎖的同步代碼塊,並對該對象調用了wait()方法,釋放自己擁有的所有鎖標記,進入該對象等待隊列,另一個線程獲得了該對象的鎖標記,進入代碼塊對該對象調用了notify()方法,就會從等待隊列裏釋放出一線程,釋放出的這個線程要繼續運行就還要進入那個同步代碼塊,因為得不到要訪問代碼塊對象的鎖標記,而進入該對象的鎖池,等待鎖標記釋放。
什麽情況下釋放鎖:
1,同類代碼執行完畢。
2,異常未處理,錯誤退出。
3,調用wait()。
相關方法:
1) wait():交出鎖和CPU的占用;
2) notify():將從對象的等待池中移走一個任意的線程,並放到鎖池中,那裏的對象一直在等待,直到可以獲得對象的鎖標記。
3) notifyAll(): 將從等待池中移走所有等待那個對象的線程並放到鎖池中,只有鎖池中的線程能獲取對象的鎖標記,鎖標記允許線程從上次因調用wait()而中斷的地方開始繼續運行
- 註意:
用notifyAll()取代notify(),因為在調用notify()方法時,是由系統決定釋放出哪個線程。
只能對加鎖的資源進行wait()和notify()。
判斷是否進行等待wait()時,用while代替if來進行判斷。
多線程的同步