java淺談鎖

    當使用synchronized關鍵字時,實際上是獲得了當前對象上的鎖。在Java中,每一個對象都有與之關聯的鎖。這個關鍵字可以用到任意的代碼塊中。例如,將一段代碼包含到synchronized塊中,就可以在這段代碼中提供原子操作,如下所示:

      synchronized (this){
                //program statements;

    }

同步語句獲得了由this關鍵字制定的當前對象上的鎖,並且爲原子操作來執行包含在花括號中的所有程序語句。this僅僅是指向當前對象的引用,此外,還可以使用指向其他對象的引用,程序將獲得制定對象上的鎖。(在獲得線程執行塊的整個期間,將會鎖定整個對象。因此,爲了調用這個對象的其他方法,其他任何的線程都不得不等待)

     調用synchronized方法的順序不當,可能造成死鎖,不恰當或者過度的同步還會導致應用程序的效率的降低

      比喻:

            有兩個運行在不同處理器上的線程,兩者都可以訪問某個通用變量,很明顯,這個變量存在於機器的通用主存中,兩個處理器都可能緩存這個變量。因此,在沒有同步的情況下,從處理器各自的緩存中讀取,對於同一個通用變量,這兩個線程可能看見不同的值。當對監視器同步時,在獲得鎖之後,Java內存模型(Java Memory Model,JMM)要求緩存立即消失,然後在釋放之前刷新緩存,頻繁的刷新緩存可能很耗費資源,這就解釋了使用同步時的性能損失問題。

      即使當程序中只包含運行在單個處理器的單個線程上時候,同步方法的調用也要比非同步方法的調用耗費更多的時間。如果同步需要競爭鎖,那麼很大程度上提高性能的損失。這是因爲在獲得鎖之前,系統中可能發生若干個線程切換和系統調用。

     最佳選擇方法:多線程程序需要足夠的同步以便保護共享數據,避免他們受到破壞,但不要過分同步,進而避免死鎖或者性能上的下降,需要在兩者之間平衡,使用volatile關鍵字的結果是以更高效的方法實現同步。

private int sharedVariable;
	
	public synchronized int getSharedVariable(){
		return sharedVariable;
	}
	
	public synchronized void setSharedVariable(int sharedVariable){
		this.sharedVariable=sharedVariable;
	}
下面的代碼給出更好的方式:

private volatile int sharedVariable;
	
	public  int getSharedVariable(){
		return sharedVariable;
	}
	
	public  void setSharedVariable(int sharedVariable){
		this.sharedVariable=sharedVariable;
	}
現在,我們將變量標記爲volatile,而不是將訪問方法設置爲synchronized,這樣做更高效,因爲volatile只是在線程和內存之間同步變量的值,而同步方法除了要鎖定和釋放監視器外,還要爲線程的所有變量在線程和主存之間提供同步。

死鎖:當兩個或者多個線程競爭以獲得某個共享資源的鎖時,沒有線程能夠繼續執行,除非其他的線程將自己的持有的鎖釋放。死鎖發生後,所有的競爭線程都不能繼續。

死鎖的解決辦法;

     1.鎖的順序

     2. 鎖超時,

     3. 死鎖檢測

1鎖的順序:如果所有的線程都以同樣的順序來或者和釋放多個對象的鎖,那麼死鎖就不會發生。一般規則是:存在多個鎖的情況下,如果任意線程總是按照相同順序來獲得所有的鎖,死鎖就不可能發生,但是,並不是總是能夠在獲得任何鎖之前都知道所需要的鎖。

2鎖超時:在給定的超時期限內,如果線程沒有成功的獲得所必須的鎖,線程將回退,釋放目前所有獲得的鎖,然後在等待隨機 的時間內嘗試再次獲得鎖。這種隨機的等待時間給了其他的線程獲得鎖的公平機會。

          

Lock lock=new ReentrantLock();
	lock.lock();
	lock.unlock();
可以使用tryLock(long timeout,TimeUnit timeUnit)方法來指定試圖獲取某個鎖的超時時間,包含在lock(),unlock()之間的語句代碼會作爲原子操作執行,並且從不損壞系統的完整性

3 死鎖檢測:首先記錄所有的線程的每一個請求和獲得每一個鎖,通常情況下,爲了方便遍歷,他們被存儲在映射或者視圖中,當對鎖的請求被拒絕後,線程會遍歷這個鎖圖來檢查死鎖。 

      解決辦法:1 釋放所有的鎖並且撤回所有等待的請求,然後在每一個線程試圖獲取所期望的鎖之前等待某個隨機訪問。這無法保證重新獲得鎖的第二次嘗試會成功,並且我們可能需要重複整個過程若干次,尤其當被調用的線程的數目非常大的時候。

                       2 進行基於優先級的回退,只是回退幾個被分配了較低優先級的線程,而其他的線程繼續持有他們的鎖。當檢測到死鎖時,這些回退的線程自身的優先級可能是隨機分配的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章