1. 程式人生 > >synchronized 作為悲觀鎖,鎖住了什麼?

synchronized 作為悲觀鎖,鎖住了什麼?

![](https://img.hacpai.com/bing/20190711.jpg?imageView2/1/w/960/h/520/interlace/1/q/100) 繼續來認識 **synchronized**,上篇文章[加不加 synchronized 有什麼區別?](https://mp.weixin.qq.com/s/-fv3uDIf9zADtU0Vue8SsA)我們瞭解了 **synchronized** 是在多執行緒併發競爭同一資源的時候使用,這一篇我們來了解,`synchronized 作為悲觀鎖,鎖住了什麼?` ### 鎖例項物件 上篇文章我們就有鎖例項物件的程式碼樣例,只是當時沒有細說這個概念。我們再寫一個程式碼來測試一下。程式碼邏輯是這樣的:我們寫 2 個 **synchronized** 例項方法,讓 5 個執行緒隨機執行 2 個方法。程式碼如下: #### 只有一個例項物件的情況: ```java public class SynchronizedTest { public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); for (int i = 0; i < 5; i++) { int thisi = i; Thread thread = new Thread(() -> { if (thisi % 2 == 0) { synchronizedTest.testSynchronizedMethod1(); } else { synchronizedTest.testSynchronizedMethod2(); } }); thread.start(); } } public synchronized void testSynchronizedMethod1() { System.out.printf("%s-testSynchronizedMethod1-start-count=%s\n", Thread.currentThread().getName(), count); count ++; System.out.printf("%s-testSynchronizedMethod1-end-count=%s\n", Thread.currentThread().getName(), count); } public synchronized void testSynchronizedMethod2() { System.out.printf("%s-testSynchronizedMethod2-start-count=%s\n", Thread.currentThread().getName(), count); count ++; System.out.printf("%s-testSynchronizedMethod2-end-count=%s\n", Thread.currentThread().getName(), count); } } ``` 執行結果: ![](http://www.liebrother.com/upload/4f2bf332ed1a4cd095057a920372b686_image.png) 這份程式碼裡面有 5 個執行緒競爭一個 synchronizedTest 資源,所以只能序列跑,我們這裡用了 2 個方法,為了讓大家更清楚的明白鎖的是物件,而不是鎖物件裡面的某個方法。如果是鎖方法,那麼執行緒**Thread-0**呼叫`testSynchronizedMethod1`方法和執行緒**Thread-1**呼叫`testSynchronizedMethod2`方法就不會序列執行,會併發執行;但是結果是序列執行,也就驗證了是鎖 synchronizedTest 物件而不是方法。通過 count 結果,更加清晰的瞭解,方法是序列執行的。 #### 每個執行緒一個例項物件的情況: ```java public class SynchronizedTest { public static void main(String[] args) { for (int i = 0; i < 5; i++) { SynchronizedTest synchronizedTest = new SynchronizedTest(); int thisi = i; Thread thread = new Thread(() -> { if (thisi % 2 == 0) { synchronizedTest.testSynchronizedMethod1(); } else { synchronizedTest.testSynchronizedMethod2(); } }); thread.start(); } } public synchronized void testSynchronizedMethod1() { System.out.printf("%s-testSynchronizedMethod1-start-count=%s\n", Thread.currentThread().getName(), count); count ++; System.out.printf("%s-testSynchronizedMethod1-end-count=%s\n", Thread.currentThread().getName(), count); } public synchronized void testSynchronizedMethod2() { System.out.printf("%s-testSynchronizedMethod2-start-count=%s\n", Thread.currentThread().getName(), count); count ++; System.out.printf("%s-testSynchronizedMethod2-end-count=%s\n", Thread.currentThread().getName(), count); } } ``` ![](http://www.liebrother.com/upload/1b4038e8a3f949e6937d0d980239f891_image.png) 這份程式碼是每個執行緒有獨立的物件,執行緒之間沒有競爭,所以互不影響,count 也都是執行緒獨立的,所以 end 結果都是 1。這個例子為了和下面的鎖類的 Class 物件做對比,先記住鎖例項物件的情況,只要執行緒之間鎖的不是同一個例項物件,執行緒之間就沒有競爭。 ### 鎖類的 Class 物件 我們將本來例項方法改成 static 靜態方法,這份程式碼 IDE 會提示異常,咱先忽略異常,可以執行成功。 ```java public class SynchronizedTest { public static void main(String[] args) { for (int i = 0; i < 5; i++) { SynchronizedTest synchronizedTest = new SynchronizedTest(); Thread thread = new Thread(() -> { synchronizedTest.testSynchronizedStaticMethod(); }); thread.start(); } } public static synchronized void testSynchronizedStaticMethod() { System.out.println("testSynchronizedStaticMethod-start-" + Thread.currentThread().getName()); System.out.println("testSynchronizedStaticMethod-end-" + Thread.currentThread().getName()); } } ``` 程式碼結果: ![](http://www.liebrother.com/upload/d3b9ae83a6b64378a68fe9a70450ce61_image.png) 我們可以看出,5 個執行緒執行到`testSynchronizedStaticMethod`方法都是序列執行,這和上面的例子有點差別,上面例子 5 個例項物件的情況下是互不影響的。所以可以看出,加上 static 不是鎖例項物件,而是**鎖 Class 物件**。 ### 總結 這一篇我們講了 **synchronized** 修飾方法時的 2 種鎖機制:鎖例項物件和鎖類的 Class 物件。從鎖的**粗粒度**來對比,鎖類 Class 物件的粒度大於鎖例項物件。 同步方法是 **synchronized** 最簡單的用法,接下來一篇會講 **synchronized** 的程式碼塊用法,程式碼塊用法把鎖粒度再降一個檔次,期待你到時閱讀。 [加不加 synchronized 有什麼區別?](https://mp.weixin.qq.com/s/-fv3uDIf9zADtU0Vue8SsA) [從 JVM 視角看看 Java 守護執行緒](http://www.liebrother.com/daemon) [寫了那麼多年 Java 程式碼,終於 debug 到 JVM 了](https://mp.weixin.qq.com/s/GsdMqNAHGY4VoLtA0nSXEA) [全網最新最簡單的 openjdk13 程式碼編譯](https://mp.weixin.qq.com/s/5gO0YJZB7huG9cOByH5Qow) [瞭解Java執行緒優先順序,更要知道對應作業系統的優先順序,不然會踩坑](https://mp.weixin.qq.com/s/Fh8d9ITNhvwYlfF2kY8P-g) [執行緒最最基礎的知識](https://mp.weixin.qq.com/s/NSlEeXMK22-clfDv44h60w) [老闆叫你別阻塞了](https://mp.weixin.qq.com/s/cIj_uzT6gZjROO44rNFHFQ) [吃個快餐都能學到序列、並行、併發](https://mp.weixin.qq.com/s/Euc2NKvK_TsqvcT-DWpD5A) [泡一杯茶,學一學同異步](https://mp.weixin.qq.com/s/yWqFw_S7suYpqszuJFDsGg) [程序知多少?](https://mp.weixin.qq.com/s/HJIVxnzyDesYPGGyJsaFyQ) [設計模式看了又忘,忘了又看?](https://mp.weixin.qq.com/s/WiPwb7AyVlxyr1_kYXt96w) **後臺回覆『設計模式』可以獲取《一故事一設計模式》電子書** `覺得文章有用幫忙轉發&點贊,多謝朋友們!` ![LieBrother](http://www.liebrother.com/upload/c50a23a8826d45a7b66b3be24c8920