synchronized 程式碼塊怎麼用
阿新 • • 發佈:2020-04-28
![](http://www.liebrother.com/upload/d84c8330b13b46588a58382158eeec87_image.png)
[加不加 synchronized 有什麼區別?](https://mp.weixin.qq.com/s/-fv3uDIf9zADtU0Vue8SsA)
[synchronized 作為悲觀鎖,鎖住了什麼?](https://mp.weixin.qq.com/s/VlciD2XnHtsG3BC-z6K7ZA)
之前 2 篇文章我們已經知道 **synchronized** 的使用方法以及鎖的內容(例項物件和Class類物件),這已經涵蓋了這個關鍵字的基本內容了,今天這篇想介紹一下另一種寫法,就是**同步程式碼塊**,它實現了更細粒度的同步方式。下面來見分曉。
先給大家介紹一下同步程式碼塊怎麼寫,大體的程式碼框架是這樣:
```java
synchronized(xxx) {
}
```
xxx 可以是 this 或者 Object 或者 xxx.class,下面我們就根據這 3 種不同加鎖方式進行展開討論。
### this
表示的是鎖住當前物件,和原來使用同步例項方式一樣,鎖住了當前的物件。
```java
public class SynchronizedCodeTest {
public static void main(String[] args) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
for (int i = 0; i < 5; i ++) {
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchronizedCode();
});
thread.start();
}
}
int count = 0;
public void testSynchronizedCode() {
System.out.printf("%s-testSynchronizedCode-start-count=%s\n", Thread.currentThread().getName(), count);
synchronized (this) {
System.out.printf("%s-testSynchronizedCode-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
count ++;
System.out.printf("%s-testSynchronizedCode-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
}
System.out.printf("%s-testSynchronizedCode-end-count=%s\n", Thread.currentThread().getName(), count);
}
}
```
執行結果:
![](http://www.liebrother.com/upload/490ed4fc3c354818b3e05e55b5d35ded_image.png)
我們主要關注紅色框和藍色框的這部分結果,紅色框裡面是同步塊的程式碼,執行緒之間是互斥的,但是藍色框中**Thread-0**在執行同步塊的過程中,其他執行緒非同步塊也在執行,這裡說明了鎖的粒度確實變小了,變成了方法裡面的同步塊程式碼之間互斥,非同步塊程式碼不互斥,count 的值最終是 5,說明到執行到同步塊時,同一時刻只有一個執行緒在執行。
我們再寫個測試程式碼,看一看 `synchronized(this)` 是鎖住什麼?
```java
public class SynchronizedCodeTest {
public static void main(String[] args) {
for (int i = 0; i < 5; i ++) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchronizedCode();
});
thread.start();
}
}
}
```
執行結果:
![](http://www.liebrother.com/upload/2654b5db0fc14784a2eb20e73458d7c6_image.png)
觀察到紅色框裡面就可以發現,這時候同步塊不起效果了,並且 count 最終都是 1,證明 `synchronized(this)` 鎖住的是當前的物件,和 `public synchronized void testSynchronizedMethod()` 一樣。
### Object
同步程式碼塊帶來了靈活性,它不再只是鎖住當前物件了,可以鎖住任何我們建立的物件,下面就來看看。
```java
public class SynchronizedCodeTest {
public static void main(String[] args) {
Object lock = new Object();
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
for (int i = 0; i < 5; i ++) {
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchroniedLock();
});
thread.start();
}
}
int count = 0;
Object lock = null;
public SynchronizedCodeTest(Object lock) {
this.lock = lock;
}
public void testSynchroniedLock() {
System.out.printf("%s-testSynchroniedLock-start-count=%s\n", Thread.currentThread().getName(), count);
synchronized (lock) {
System.out.printf("%s-testSynchroniedLock-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
count ++;
System.out.printf("%s-testSynchroniedLock-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
}
System.out.printf("%s-testSynchroniedLock-end-count=%s\n", Thread.currentThread().getName(), count);
}
}
```
執行結果:
![](http://www.liebrother.com/upload/5790d3d221ae440a83ca929b5ecc4c39_image.png)
這段程式碼,我們建立了一個 lock 物件,作為引數傳入到 synchronizedCodeTest 物件裡,我們看到結果裡面,5 個執行緒在同步塊程式碼裡是序列執行的,count 最終也得到結果是 5。這段程式碼沒有看出鎖物件帶來的靈活性,下面再看一個例子,把測試程式碼稍微改一下,讓每個執行緒都有自己的 synchronizedCodeTest 物件。
```java
public static void main(String[] args) {
Object lock = new Object();
for (int i = 0; i < 5; i ++) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest(lock);
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchroniedLock();
});
thread.start();
}
}
```
執行結果:
![](http://www.liebrother.com/upload/d1b3abb8b71c41dabc3010e8ad05cea2_image.png)
結果我們發現,雖然我們為每個執行緒建立一個 synchronizedCodeTest 物件,但是不管怎麼執行,5 個執行緒的同步程式碼塊都是序列執行的,原因在於,我們只建立了一個 lock 物件,這 5 個 synchronizedCodeTest 的 lock 物件都是同一個,因此競爭資源是同一個,才出現這種情況。看是不是比同步方法靈活多了,上一篇中,我們要讓多個例項同步執行,我們需要使用**靜態同步方法**,現在不用了,使用同步程式碼塊就可以,只需要滿足鎖住同一個例項物件就行。
另外,這個例子的結果是每個例項的 count 最終都為 1,這是因為每個 synchronizedCodeTest 物件都有自己獨立的變數 count,所以執行緒之間互不影響。
### xxx.class
再來看看最後一種程式碼塊鎖 Class 類,這和 `public static synchronized testSynchronizedStatic()` 的作用是一樣的,區別就只是程式碼塊的鎖範圍可變。我們直接看看程式碼例子。
```java
public class SynchronizedCodeTest {
public static void main(String[] args) {
for (int i = 0; i < 5; i ++) {
SynchronizedCodeTest synchronizedCodeTest = new SynchronizedCodeTest();
Thread thread = new Thread(() -> {
synchronizedCodeTest.testSynchronizedCodeClass();
});
thread.start();
}
}
int count = 0;
public void testSynchronizedCodeClass() {
System.out.printf("%s-testSynchronizedCodeClass-start-count=%s\n", Thread.currentThread().getName(), count);
synchronized (SynchronizedCodeTest.class) {
System.out.printf("%s-testSynchronizedCodeClass-synchronized-start-count=%s\n", Thread.currentThread().getName(), count);
count ++;
System.out.printf("%s-testSynchronizedCodeClass-synchronized-end-count=%s\n", Thread.currentThread().getName(), count);
}
System.out.printf("%s-testSynchronizedCodeClass-end-count=%s\n", Thread.currentThread().getName(), count);
}
}
```
執行結果:
![](http://www.liebrother.com/upload/00994994bc564777897b261b409bb422_image.png)
每個執行緒都有自己的例項,但是鎖住 Class 會使每個執行緒例項物件的同步塊都是序列執行,這個結果和上面的多個例項鎖住同一個 Object 物件的結果是一樣的。我個人更傾向於使用鎖同一個 Object 物件,而不是鎖 Class 類物件。
### 總結
這篇介紹了**synchronizd 程式碼塊**的 3 種使用方式,並詳細介紹了各自的使用方式和區別。簡單的列個表。
|型別|使用方式|鎖作用範圍|
|:---:|:---:|:---:|
| this | synchronized(this){} |鎖住當前的例項物件 |
| object | synchronized(lock){}| 鎖住其他例項物件,比較靈活 |
| xxx.class | synchronized(xxx.class){} | 鎖住 Class 物件 |
總共用了 3 篇文章來給大家介紹 `synchronized` 的具體用法,主要是因為之前有些文章一下子就進入 Java 原始碼和 JVM 原始碼,讓不少朋友感覺有點吃力,甚至有朋友一看標題就不進來看看了,所以這次先介紹一些比較簡單的用法,讓大家先了解了解,後面幾篇會比較深入 `synchronized` 原理性的文章,希望大家也能看看,會有非常大的收穫的。
原創不易,大家多幫忙轉發,點在看。
[synchronized 作為悲觀鎖,鎖住了什麼?](https://mp.weixin.qq.com/s/VlciD2XnHtsG3BC-z6K7ZA)
[加不加 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/c50a23a8826d45a7b66b3be24c89205