淺析Java 併發程式設計中的synchronized
synchronized關鍵字,我們一般稱之為“同步鎖”,用它來修飾需要同步的方法和需要同步程式碼塊,預設是當前物件作為鎖的物件。在用synchronized修飾類時(或者修飾靜態方法),預設是當前類的Class物件作為鎖的物件,故存在著方法鎖、物件鎖、類鎖這樣的概念。
一、沒有設定執行緒同步的情況
先給出以下程式碼感受下程式碼執行的時候為什麼需要同步?程式碼可能比較枯燥,配上業務理解起來就會舒服很多,學生軍訓,有三列,每列5人,需要報數,每個執行緒負責每一列報數。
class SynchronizedExample { protected static int num = 0; protected void numberOff() { for(int i=0; i<5; i++) { num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } } } public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { SynchronizedExample se = new SynchronizedExample(); for(int i=1; i<=3; i++) { new Thread( ()-> {se.numberOff();},"執行緒"+i).start(); } } }
執行結果如下:
執行緒1:1
執行緒2:2
執行緒1:3
執行緒3:4
.......
之所以出現這種情況,是因為三個執行緒是非同步的,沒有同步。
對應的業務場景就是,在第一列沒有完成報數的時候,其他佇列搶報了,這在現實中是不允許的,所以需要類似於synchronized等具有同步功能的關鍵字粉末登場。
二、方法同步鎖
當報數方法加上synchronized關鍵字之後,就會一列一列的報數。
protected synchronized void numberOff() { for(int i=0; i<5; i++) { num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } }
執行結果如下:
執行緒1:1
執行緒1:2
執行緒1:3
執行緒1:4
......
寫到這裡還是要從技術層面講下原理,當一個執行緒執行帶有synchronized關鍵字的方法時,該執行緒會在該方法處設定一個鎖(其他執行緒打不開這個鎖,只能在外邊等該執行緒釋放掉該鎖,一般都是執行完所有程式碼後主動釋放鎖),表示此方法是當前執行緒獨佔的,對應到上述業務中就是一次只能有一個佇列報數。
三、物件鎖
改進後的程式碼用到了一個物件鎖,該物件鎖預設是當前物件,上述程式碼等同於以下程式碼:
protected void numberOff() { synchronized (this) { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } }
當多個執行緒用一個物件鎖,各個執行緒可以達到同步的作用,如果每個執行緒都用自己的物件鎖,那麼synchronized就失去了同步的作用。如以下程式碼:
class SynchronizedExample { protected static int num = 0; protected void numberOff() { synchronized (this) { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } } } public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { SynchronizedExample se = new SynchronizedExample(); for(int i=1; i<=3; i++) { new Thread( ()-> {new SynchronizedExample().numberOff();},"佇列"+i).start(); } } }
執行結果如下:
執行緒1:1
執行緒2:2
執行緒1:3
執行緒3:4
.......
有讀者會說不同執行緒執行的是不同物件中的方法,肯定達不到同步的效果,也對,也很有道理,接著看如下程式碼:
class SynchronizedExample { protected static int num = 0; protected void numberOff(Object lock) { synchronized (lock) { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } } } public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { SynchronizedExample se = new SynchronizedExample(); for(int i=1; i<=3; i++) { new Thread( ()-> {se.numberOff(new Object());},"佇列"+i).start(); } } }
執行結果如下:
執行緒1:1
執行緒2:2
執行緒1:3
執行緒3:4
.......
四、類鎖
對於上述問題,讀者應該得出一個結論,要想達到同步的效果,必須用同一個鎖,此時類鎖可以粉末登場。看如下程式碼:
protected void numberOff(Object lock) { synchronized (SynchronizedExample.class) { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } }
上述程式碼可以達到同步的效果。
五、靜態鎖
靜態鎖是針對靜態方法而言,當一個靜態方法中有synchronized關鍵字時,預設的是使用當前類位元組碼物件作為鎖。程式碼示例如下:
class SynchronizedExample { protected static int num = 0; protected synchronized static void numberOff() { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } } public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 3; i++) { new Thread(() -> { new SynchronizedExample().numberOff(); },"佇列" + i).start(); } } }
六、執行緒池實現
最後用執行緒池將上述程式碼寫一下
package ioo; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class SynchronizedExample { protected static int num = 0; protected synchronized static void numberOff() { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } } public class SynchronizedTest { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); for(int i=1; i<=3; i++) { executorService.execute(() -> new SynchronizedExample().numberOff()); } } }
以上就是淺析Java 併發程式設計中的synchronized的詳細內容,更多關於Java 併發程式設計 synchronized的資料請關注我們其它相關文章!