淺析Java 并發(fā)編程中的synchronized
synchronized關(guān)鍵字,我們一般稱之為“同步鎖”,用它來修飾需要同步的方法和需要同步代碼塊,默認是當前對象作為鎖的對象。在用synchronized修飾類時(或者修飾靜態(tài)方法),默認是當前類的Class對象作為鎖的對象,故存在著方法鎖、對象鎖、類鎖這樣的概念。
一、沒有設置線程同步的情況
先給出以下代碼感受下代碼執(zhí)行的時候為什么需要同步?代碼可能比較枯燥,配上業(yè)務理解起來就會舒服很多,學生軍訓,有三列,每列5人,需要報數(shù),每個線程負責每一列報數(shù)。
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(); } } }
執(zhí)行結(jié)果如下:
線程1:1
線程2:2
線程1:3
線程3:4
.......
之所以出現(xiàn)這種情況,是因為三個線程是異步的,沒有同步。
對應的業(yè)務場景就是,在第一列沒有完成報數(shù)的時候,其他隊列搶報了,這在現(xiàn)實中是不允許的,所以需要類似于synchronized等具有同步功能的關(guān)鍵字粉末登場。
二、方法同步鎖
當報數(shù)方法加上synchronized關(guān)鍵字之后,就會一列一列的報數(shù)。
protected synchronized void numberOff() { for(int i=0; i<5; i++) { num++; System.out.println(Thread.currentThread().getName()+":"+SynchronizedExample.num); } }
執(zhí)行結(jié)果如下:
線程1:1
線程1:2
線程1:3
線程1:4
......
寫到這里還是要從技術(shù)層面講下原理,當一個線程執(zhí)行帶有synchronized關(guān)鍵字的方法時,該線程會在該方法處設置一個鎖(其他線程打不開這個鎖,只能在外邊等該線程釋放掉該鎖,一般都是執(zhí)行完所有代碼后主動釋放鎖),表示此方法是當前線程獨占的,對應到上述業(yè)務中就是一次只能有一個隊列報數(shù)。
三、對象鎖
改進后的代碼用到了一個對象鎖,該對象鎖默認是當前對象,上述代碼等同于以下代碼:
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(); } } }
執(zhí)行結(jié)果如下:
線程1:1
線程2:2
線程1:3
線程3:4
.......
有讀者會說不同線程執(zhí)行的是不同對象中的方法,肯定達不到同步的效果,也對,也很有道理,接著看如下代碼:
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(); } } }
執(zhí)行結(jié)果如下:
線程1:1
線程2:2
線程1:3
線程3:4
.......
四、類鎖
對于上述問題,讀者應該得出一個結(jié)論,要想達到同步的效果,必須用同一個鎖,此時類鎖可以粉末登場。看如下代碼:
protected void numberOff(Object lock) { synchronized (SynchronizedExample.class) { for (int i = 0; i < 5; i++) { num++; System.out.println(Thread.currentThread().getName() + ":" + SynchronizedExample.num); } } }
上述代碼可以達到同步的效果。
五、靜態(tài)鎖
靜態(tài)鎖是針對靜態(tài)方法而言,當一個靜態(tài)方法中有synchronized關(guān)鍵字時,默認的是使用當前類字節(jié)碼對象作為鎖。代碼示例如下:
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(); } } }
六、線程池實現(xiàn)
最后用線程池將上述代碼寫一下
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 并發(fā)編程中的synchronized的詳細內(nèi)容,更多關(guān)于Java 并發(fā)編程 synchronized的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 下
- Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 上
- Java并發(fā)之synchronized實現(xiàn)原理深入理解
- java并發(fā)編程之深入理解Synchronized的使用
- 詳解Java并發(fā)編程之內(nèi)置鎖(synchronized)
- 淺析java并發(fā)中的Synchronized關(guān)鍵詞
- Java并發(fā) synchronized鎖住的內(nèi)容解析
- 詳解java并發(fā)編程(2) --Synchronized與Volatile區(qū)別
- 詳解Java利用同步塊synchronized()保證并發(fā)安全
- java并發(fā)之synchronized
相關(guān)文章
如何在spring boot中進行參數(shù)校驗示例詳解
這篇文章主要介紹了如何在spring-boot中進行參數(shù)校驗及l(fā)ombok的使用詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05Java中==符號與equals()的使用詳解(測試兩個變量是否相等)
下面小編就為大家?guī)硪黄狫ava中==符號與equals()的使用詳解(測試兩個變量是否相等)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07