Lock與synchronized是不一樣的

2021-07-07 06:49:41 字數 3393 閱讀 6739

很多編碼者都會說,lock類和synchronized關鍵字用在**塊的併發性和記憶體上時語義 是一樣的,都是保持**塊同時只有乙個執行緒具有執行權。這樣的說法只對了一半,我們以乙個任務提交給多個執行緒執行為例,來看看使用顯式鎖(lock類)和內部鎖(synchronized 關鍵字)有什麼不同。首先定義乙個任務:

class task catch (exception e) 

stringbuffer sb=new stringbuffer();

system.out.println(sb);

}}

該類模擬了乙個執行時間比較長的計算,注意這裡使用的是模擬方式,在使用sleep 方法時執行緒的狀態會從執行狀態轉變為等待狀態。該任務要具備多執行緒能力時必須實現rmmable介面,我們分別建立兩種不同的鎖實現機制,首先看

//顯式銷實現 //乙個執行緒乙個鎖  鎖不共享  不會互斥

class taskwithlock extends task implements runnable catch (exception e) finally }

}

這裡有一點需要說明的是,顯式鎖的鎖定和釋放必須在乙個try……finally塊中,這是 為了確保即使出現執行期異禽也能正常釋放鎖,保證其他執行緒能夠順利執行。

內部鎖的處理也非常簡單,**如下:

//內部鎖任務

class taskwithsync extends task implements runnable }

}

這兩個任務看著非常相似,應該能夠產生相似的結果吧?我們建立乙個模擬場景,保證同時有三個執行緒在執行,**如下:

public static void runtasks(class <? extends runnable> clz) throws exception

//等待足夠長的時間,然後關閉執行器

timeunit.seconds.sleep(10);

system.out.println("-----"+clz.get******name()+"任務執行完畢------\n");

es.shutdown();}

public static void main(string args) throws exception

按照一般的理解,lock和synchronized的處理方式是相同的,輸出應該沒有差別,但是 很遺憾的是,輸出差別其實很大。輸出如下:

***** 開始執行taskwithlock 任務 ******

----- taskwithlock任務執行完畢-

***** 開始執行 taskwithsync任務 ******

----- taskwithsync任務執行完畢-

注意看執行的時間戰,顯式鎖是同時執行的,很顯然在pool-1-thread-l執行緒執行到 sleep時,其他兩個執行緒也會執行到這裡,一起等待,然後一起輸出,這還具有執行緒互斥的槪念嗎?

而內部鎖的輸出則是我們的預期結果:pool-2-thread-l執行緒在執行時其他執行緒處於等待 狀態,pool-2-thread-l執行完畢後,jvm從等待執行緒池中隨機獲得乙個執行緒po

〇l-2-thread-3 執行,最後再執行pool-2-thread-2,這正是我們希望的。

現在問題來了: lock鎖為什麼不出現互斥情況呢?

這是因為對於同步資源來說(示例中是**塊),顯式鎖是物件級別的鎖,而內部鎖是類級別的鎖,也就是說lock鎖是跟隨物件的,synchronized鎖是跟隨類的,更簡單地說把 lock定義為多執行緒類的私有屬性是起不到資源互斥作用的,除非是把lock定義為所有執行緒 的共享變數。都說**是最好的解釋語言,我們來看乙個lock鎖資源的**:

public static void main(string args) throws exception  catch (interruptedexception e) finally

}}).start();

} }

讀者可以執行一下,會發現執行緒名稱thread-0、thread-1、thread-2會逐漸輸出,也就 是乙個執行緒在執行時,其他執行緒就處於等待狀態。注意,這裡三個執行緒執行的例項物件是同乙個類(都是client$l類的例項)。

那除了這一點不同之外,顯式鎖和內部鎖還有什麼不同呢?還有以下4點不同:

(1) lock支援更細粒度的鎖控制

假設讀寫鎖分離,寫操作時不允許有讀寫操作存在,而讀操作時讀寫可以併發執行,這一點內部鎖就很難實現。顯式鎖的示例**如下:

class foocatch (interruptedexception e)  finally 

} //寫操作,同時只允許乙個寫操作

public void write catch(interruptedexception e) finally

}}

可以編寫乙個runnable的實現類,把foo類作為資源進行呼叫(注意多執行緒是共享這 個資源的),然後就會發現這樣的現象:讀寫鎖允許同時有多個讀操作但只允許有乙個寫操作,也就是當有乙個寫執行緒在執行時,所有的讀執行緒和寫執行緒都會阻塞,直到寫執行緒釋放鎖 資源為止,而讀鎖則可以有多個執行緒同時執行。

(2) 

lock是無阻塞鎖,synchronized是阻塞鎖

當執行緒a持有鎖時,執行緒b也期望獲得鎖,此時,如果程式中使用的是顯式鎖,則b 執行緒為等待狀態(在通常的描述中,也認為此執行緒被阻塞了),若使用的是內部鎖則為阻塞狀態。

(3)  lock可實現公平鎖,synchronized只能是非公平鎖

什麼叫非公平鎖呢?當乙個執行緒a持有鎖,而執行緒b、c處於阻塞(或等待)狀態時, 若執行緒a釋放鎖,jvm將從執行緒b、c中隨機選擇乙個執行緒持有鎖並使其獲得執行權,這叫 做非公平鎖(因為它拋棄了先來後到的順序);若jvm選擇了等待時間最長的乙個執行緒持有 鎖,則為公平鎖(保證每個執行緒的等待時間均衡)。需要注意的是,即使是公平鎖,jvm也 無法準確做到「公平」,在程式中不能以此作為精確計算。

顯式鎖預設是非公平鎖,但可以在建構函式中加入引數true來宣告出公平鎖,而 synchronized實現的是非公平鎖,它不能實現公平鎖。

(4) lock是**級的,synchronized是jvm級的

lock是通過編碼實現的,synchronized是在執行期由jvm解釋的,相對來說

synchronized的優化可能性更髙,畢竟是在最核心部分支援的,lock的優化則需要使用者自行

考慮。顯式鎖和內部鎖的功能各不相同,在效能上也稍有差別,但隨著jdk的不斷推進,相 對來說,顯式鎖使用起來更加便利和強大,在實際開發中選擇哪種型別的鎖就需要根據實際情況考慮了:靈活、強大則選擇lock,快捷、安全則選擇synchronized。

注意兩種不同的鎖機制,根據不同的情況來選擇。

Java執行緒同步問題synchronized

android usb 讀寫以前都是一讀一寫,但有些機器會出問題。就採用讀寫非同步的方法。使用物件鎖,object自帶的,然後使用object的方法wait和notify notifyall 使用方法簡單,記錄下 public synchronized int lra setregister int...

Java學習之執行緒鎖 synchronized

同步 併發 多個執行緒訪問同乙份資源 確保資源安全 執行緒安全 synchronized 同步 1 同步塊 synchronized 引用型別 this 類.class 2 同步方法 public synchronized void test public class testsyn class t...

Lock(遞迴函式與死鎖)

看看以下會不會產生死鎖 public class a else class program 你的答案是會產生死鎖嗎?可以理解,因為也許你以前一直以為外層遞迴被鎖住後,不允許在訪問裡邊的 快,而由於遞迴,有需要等待外層遞迴解鎖,所以由此造成死鎖,現在才知道這種理解是錯誤的 結果是永遠不會出現死鎖,因為...