1. 程式人生 > >多線程的同步

多線程的同步

調用 har 什麽 同步 同步代碼塊 span 並行 進入 地方

多線程並發訪問同一個對象(臨界資源),如果不對線程進行同步控制,破壞了原子操作(不可再分的操作),則會造成臨界資源(兩個線程同時訪問的資源)的數據不一致。

每一個對象都有一個互斥的鎖標記和一個鎖池。當線程擁有這個對象的鎖標記時才能訪問這個資源,沒有鎖標記便進入鎖池,保證在同步代碼塊中只有一個線程,解決了多線程同步控制的問題。

關鍵字: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來進行判斷。

多線程的同步