在我們的操作系統上運行著很多的計算機程序,每一個計算機程序可能都包含一個或多個獨立的線程,而這些線程若訪問我們系統上的共享資源時,比如說:內存資源、文件資源、數據庫資源等。可能會使得各線程之間訪問資源時產生沖突。那我們如何來解決這種問題呢?Java中存在這么幾種處理方式:同步代碼塊、同步方法、同步鎖等。
一、同步代碼塊
同步代碼塊格式:synchronized(key){
//存放同步代碼塊;
}
如何來理解同步代碼塊呢?打個比方:一個object就像一個大房子,大門永遠打開。房子里有很多房間(也就是方法)。這些房間有上鎖的(synchronized方法),和不上鎖之分(普通方法)。房門口放著一把鑰匙(key),這把鑰匙可以打開所有上鎖的房間。另外我把所有想調用該對象方法的線程比喻成想進入這房子某個房間的人。所有的東西就這么多了,下面我們看看這些東西之間如何作用的。
一個人想進入某間上了鎖的房間,他來到房子門口,看見鑰匙在那兒(說明暫時還沒有其他人 要使用上鎖的房間)。于是他走上去拿到了鑰匙,并且按照自己的計劃使用那些房間。注意一點,他每次使用完一次上鎖的房間后會馬上把鑰匙還回去。即使他要連 續使用兩間上鎖的房間,中間他也要把鑰匙還回去,再取回來。
因此,普通情況下鑰匙的使用原則是:“隨用隨借,用完即還。”
這時其他人可以不受限制的使用那些不上鎖的房間,一個人用一間可以,兩個人用一間也可以,沒限制。但是如果當某個人想要進入上鎖的房間,他就要跑到大門口去看看了。有鑰匙當然拿了就走,沒有的話,就只能等了。
當多線程搶占方法體內的資源時,只有獲取key這個對象的線程才可以獲得并執行方法體中的內容,其它的線程必須等待這個線程執行完畢后歸還key對象,才可以申請獲得這個key來搶占資源。
二、同步方法
同步方法的語法格式:public synchronized 數據返回類型 方法名(){} 。就是使用 synchronized 這個關鍵字來修飾某個方法,則該方法稱為同步方法。對于同步方法而言,無需顯示指定同步監視器,這個同步監視器也就是同步代碼塊中的“key”;同步方法的同步監視器是 this 也就是當前對象,通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特征:
1,該類的對象可以被多個線程安全的訪問。
2,每個線程調用該對象的任意方法之后,都將得到正確的結果。
3,每個線程調用該對象的任意方法之后,該對象狀態依然保持合理狀態。
注:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等。
三、同步鎖
在Jdk1.5之后,java提供了一個強大的線程同步機制:通過顯式定義同步鎖對象來實現同步,在這種機制下,同步鎖由Lock對象充當。
鎖,提供了對共享資源的獨占訪問,每次只能有一個線程對Lock對象加鎖,線程開始訪問共享資源之前應該首先獲Lock對象。
某些鎖可能允許對共享資源并發訪問,如ReadWriteLock(讀寫鎖)。Jdk1.8之后新增了新型的StampedLock。它在大多數場景中可以替代傳統的ReentranReadWriteLock。為讀寫操作提供三種鎖模式:Writing、ReadingOptimistic、Reading。
當產生Lock對象時,我們可以直接通過Lock對象的lock()方法和unlock()方法就可以實現對資源的上鎖和解鎖過程。