淺析Java 并發(fā)編程中的synchronized
synchronized關(guān)鍵字,我們一般稱之為“同步鎖”,用它來修飾需要同步的方法和需要同步代碼塊,默認(rèn)是當(dāng)前對象作為鎖的對象。在用synchronized修飾類時(或者修飾靜態(tài)方法),默認(rèn)是當(dāng)前類的Class對象作為鎖的對象,故存在著方法鎖、對象鎖、類鎖這樣的概念。
一、沒有設(shè)置線程同步的情況
先給出以下代碼感受下代碼執(zhí)行的時候為什么需要同步?代碼可能比較枯燥,配上業(yè)務(wù)理解起來就會舒服很多,學(xué)生軍訓(xùn),有三列,每列5人,需要報數(shù),每個線程負(fù)責(zé)每一列報數(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īng)的業(yè)務(wù)場景就是,在第一列沒有完成報數(shù)的時候,其他隊列搶報了,這在現(xiàn)實中是不允許的,所以需要類似于synchronized等具有同步功能的關(guān)鍵字粉末登場。
二、方法同步鎖
當(dāng)報數(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ù)層面講下原理,當(dāng)一個線程執(zhí)行帶有synchronized關(guān)鍵字的方法時,該線程會在該方法處設(shè)置一個鎖(其他線程打不開這個鎖,只能在外邊等該線程釋放掉該鎖,一般都是執(zhí)行完所有代碼后主動釋放鎖),表示此方法是當(dāng)前線程獨占的,對應(yīng)到上述業(yè)務(wù)中就是一次只能有一個隊列報數(shù)。
三、對象鎖
改進(jìn)后的代碼用到了一個對象鎖,該對象鎖默認(rèn)是當(dāng)前對象,上述代碼等同于以下代碼:
protected void numberOff() {synchronized (this) {for (int i = 0; i < 5; i++) {num++;System.out.println(Thread.currentThread().getName() + ':' + SynchronizedExample.num);}}}
當(dāng)多個線程用一個對象鎖,各個線程可以達(dá)到同步的作用,如果每個線程都用自己的對象鎖,那么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í)行的是不同對象中的方法,肯定達(dá)不到同步的效果,也對,也很有道理,接著看如下代碼:
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.......
四、類鎖
對于上述問題,讀者應(yīng)該得出一個結(jié)論,要想達(dá)到同步的效果,必須用同一個鎖,此時類鎖可以粉末登場。看如下代碼:
protected void numberOff(Object lock) {synchronized (SynchronizedExample.class) {for (int i = 0; i < 5; i++) {num++;System.out.println(Thread.currentThread().getName() + ':' + SynchronizedExample.num);}}}
上述代碼可以達(dá)到同步的效果。
五、靜態(tài)鎖
靜態(tài)鎖是針對靜態(tài)方法而言,當(dāng)一個靜態(tài)方法中有synchronized關(guān)鍵字時,默認(rèn)的是使用當(dāng)前類字節(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的詳細(xì)內(nèi)容,更多關(guān)于Java 并發(fā)編程 synchronized的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. IDEA EasyCode 一鍵幫你生成所需代碼2. IntelliJ IDEA設(shè)置條件斷點的方法步驟3. Java構(gòu)建JDBC應(yīng)用程序的實例操作4. Spring應(yīng)用拋出NoUniqueBeanDefinitionException異常的解決方案5. ThinkPHP5 通過ajax插入圖片并實時顯示(完整代碼)6. javascript設(shè)計模式 ? 建造者模式原理與應(yīng)用實例分析7. 一篇文章帶你了解JavaScript-對象8. Python使用oslo.vmware管理ESXI虛擬機的示例參考9. Express 框架中使用 EJS 模板引擎并結(jié)合 silly-datetime 庫進(jìn)行日期格式化的實現(xiàn)方法10. 使用AJAX(包含正則表達(dá)式)驗證用戶登錄的步驟
