Java中synchronized的四種用法詳解
問(wèn)題描述
介紹之前我們先來(lái)看下,在java 多線程中 如果沒有線程同步會(huì)出現(xiàn)什么問(wèn)題:
下面這個(gè)是一個(gè)測(cè)試?yán)樱?/p>
public class MainClass { public static class MyRun implements Runnable { private int count=0; @Override public void run() { while (count<15) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; try { Thread.sleep(200); } catch (Exception e) { } } } } public static void main(String args[]) { MyRun myRun=new MyRun(); Thread threadA=new Thread(myRun,"A"); Thread threadB=new Thread(myRun,"B"); threadA.start(); threadB.start(); }}
運(yùn)行結(jié)果
ThreadName:A count:0
ThreadName:B count:0
ThreadName:A count:1
ThreadName:A count:3
ThreadName:B count:2
ThreadName:A count:4
ThreadName:A count:6
ThreadName:A count:7
ThreadName:B count:5
我們看到這個(gè)count在無(wú)序的增加,這個(gè)是由于A,B兩個(gè)線程同時(shí)操作Count變量造成的,如果我們想讓Count有序增加,應(yīng)該給
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++;
這段代碼同步枷鎖,這樣當(dāng)B線程進(jìn)入這里時(shí)候,發(fā)現(xiàn)這里已經(jīng)被鎖,就只有等待,A執(zhí)行完這段代碼之后就會(huì)釋放對(duì)這個(gè)對(duì)象的這段代碼的釋放鎖,A獲得了釋放鎖,就可以進(jìn)入執(zhí)行,讓A,B有序進(jìn)入執(zhí)行,才能讓Count有序增加,加入了synchronized之后的代碼:
while (count<15) { synchronized (this) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; } try { Thread.sleep(200); } catch (Exception e) { } }
結(jié)果:
ThreadName:A count:0
ThreadName:B count:1
ThreadName:B count:2
ThreadName:A count:3
ThreadName:A count:4
ThreadName:B count:5
ThreadName:B count:6
ThreadName:A count:7
ThreadName:B count:8
加了鎖之后A,B線程就可以有序的交替執(zhí)行,不會(huì)同時(shí)搶占執(zhí)行Count++ 操作,
下面介紹synchronised的幾種用法
1 鎖方法
public synchronized void dodo(){ }
這個(gè)就是鎖方法,這里面要注意兩點(diǎn):
synchronized 關(guān)鍵子不是方法的一部分,所以它不會(huì)被繼承,說(shuō)白了,就是如果父類的方法有synchronized,子類重寫這個(gè)方法,synchronized不寫也不會(huì)報(bào)錯(cuò)
synchronized 不能修飾接口
synchronized 不能修復(fù)構(gòu)造方法,但是可以修飾構(gòu)造方法里面的代碼塊
2 鎖代碼塊
鎖代碼塊就是我上面的那個(gè)例子寫法了,這個(gè)就是鎖的是某個(gè)對(duì)象中的某個(gè)代碼,讓它線程同步。
synchronized (this) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; }
我們看到這個(gè)synchronized (this) 里面的this,這里是要傳入一個(gè)對(duì)象的,如果不用this也可以,也可以在這個(gè)類里面new一個(gè)其他對(duì)象效果也是一樣的,比如
private Object obj=new Object(); @Override public void run() { while (count<15) { synchronized (obj) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; } try { Thread.sleep(200); } catch (Exception e) { } } }
這里就用了obj這個(gè)new的對(duì)象,效果和this完全一樣
3 鎖某個(gè)類
public class MainClass { public static class MyRun implements Runnable { public static int count=0; @Override public void run() { while (count<15) { synchronized (this) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; } try { Thread.sleep(200); } catch (Exception e) { } } } public synchronized void dodo() { } } public static void main(String args[]) { MyRun myRun1=new MyRun(); MyRun myRun2=new MyRun(); Thread threadA=new Thread(myRun1,"A"); Thread threadB=new Thread(myRun2,"B"); threadA.start(); threadB.start(); } }
這個(gè)代碼里面,
MyRun myRun1=new MyRun(); MyRun myRun2=new MyRun();
我new兩個(gè)MyRun,我前面說(shuō)過(guò)synchronized(this)只能鎖某個(gè)對(duì)象,就是說(shuō)threadA執(zhí)行myRun1 threadB執(zhí)行myRun2,互不干擾,synchronized只能鎖自己的run1 或者run2 不能兩個(gè)對(duì)象同時(shí)鎖到,所以執(zhí)行的結(jié)果是無(wú)序的。
ThreadName:A count:0
ThreadName:B count:0
ThreadName:B count:2
ThreadName:A count:2
ThreadName:B count:4
ThreadName:A count:4
ThreadName:A count:6
ThreadName:B count:6
ThreadName:A count:8
ThreadName:B count:8
如果我們想讓兩個(gè)線程有序執(zhí)行,這個(gè)Count++操作,而且對(duì)run1,和run2都同時(shí)鎖,應(yīng)該怎么辦呢???
答案是鎖類,鎖類的意思是不管是這個(gè)類new了多少對(duì)象,這個(gè)對(duì)象的所有方法,都會(huì)上鎖,我們更改下代碼看看結(jié)果,怎么鎖類的:
synchronized (MyRun.class) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; }
我們看到只改動(dòng)了 synchronized (MyRun.class)這里,其他代碼都不變們,這個(gè)就把這個(gè)MyRun鎖了,我們看看結(jié)果:
ThreadName:A count:0
ThreadName:B count:1
ThreadName:B count:2
ThreadName:A count:3
ThreadName:B count:4
ThreadName:A count:5
ThreadName:A count:6
ThreadName:B count:7
ThreadName:A count:8
結(jié)果是有序的,驗(yàn)證了我們的結(jié)果
4 靜態(tài)方法上的synchronized
public synchronized static void ddo()
這種方式其實(shí)和第三種效果是一樣的,都是對(duì)類起作用,因?yàn)槲覀冎纒tatic修飾的方法是類方法,所以這個(gè)synchronized 也是作用于整個(gè)類
我們把上面的代碼改下看看效果是不是一樣:
public class MainClass { public static class MyRun implements Runnable { public static int count=0; @Override public void run() { ddo(); } public synchronized static void ddo() { while (count<15) { System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n"); count++; try { Thread.sleep(200); } catch (Exception e) { } } } } public static void main(String args[]) { MyRun myRun1=new MyRun(); MyRun myRun2=new MyRun(); Thread threadA=new Thread(myRun1,"A"); Thread threadB=new Thread(myRun2,"B"); threadA.start(); threadB.start(); } }
結(jié)果:
ThreadName:A count:0
ThreadName:A count:1
ThreadName:A count:2
ThreadName:A count:3
ThreadName:A count:4
ThreadName:A count:5
ThreadName:A count:6
ThreadName:A count:7
ThreadName:A count:8
因?yàn)锳線程 先執(zhí)行最后滿足條件 while (count<15),所以B沒有機(jī)會(huì)執(zhí)行了,驗(yàn)證符合我們的預(yù)期
總結(jié)
1、鎖如果加在方法上面,或者在方法中的代碼塊形式,就是鎖的這個(gè)對(duì)象,如果鎖是靜態(tài)方法中,或者代碼塊synchronized(A.class) 形式 就是鎖的這個(gè)類,里面的所有方法都會(huì)同步。
2、誰(shuí)擁有了鎖,上面線程就擁有了控制這段代碼的能力,其他的線程只能看著,只有釋放了鎖,其他線程才可以操作。
3、synchronized 消耗系統(tǒng)性能,所以能不加鎖的邏輯,盡量不要加。
4、操作讀寫文件,或者數(shù)據(jù)庫(kù),有的時(shí)候多線程會(huì)出現(xiàn)不可預(yù)知的問(wèn)題,所以要加入鎖。
到此這篇關(guān)于Java中synchronized的四種用法詳解的文章就介紹到這了,更多相關(guān)synchronized的用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目如何訪問(wèn)jsp頁(yè)面的示例代碼
本篇文章主要介紹了SpringBoot項(xiàng)目如何訪問(wèn)jsp頁(yè)面的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10如何使用Playwright對(duì)Java API實(shí)現(xiàn)自動(dòng)視覺測(cè)試
這篇文章主要介紹了如何使用Playwright對(duì)Java API實(shí)現(xiàn)自動(dòng)視覺測(cè)試,幫助大家更好的理解和使用Playwright,感興趣的朋友可以了解下2021-01-01Java中遍歷ConcurrentHashMap的四種方式詳解
這篇文章主要介紹了Java中遍歷ConcurrentHashMap的四種方式詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java利用docx4j+Freemarker生成word文檔
這篇文章主要為大家詳細(xì)介紹了Java如何利用docx4j+Freemarker生成word文檔,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05創(chuàng)建網(wǎng)關(guān)項(xiàng)目(Spring Cloud Gateway)過(guò)程詳解
這篇文章主要介紹了創(chuàng)建網(wǎng)關(guān)項(xiàng)目(Spring Cloud Gateway)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09java枚舉類的屬性、方法和構(gòu)造方法應(yīng)用實(shí)戰(zhàn)
這篇文章主要介紹了java枚舉類的屬性、方法和構(gòu)造方法應(yīng)用,結(jié)合實(shí)例形式分析了java枚舉類的定義、構(gòu)造及相關(guān)應(yīng)用操作技巧,需要的朋友可以參考下2019-08-08Mybatis攔截器如何實(shí)現(xiàn)數(shù)據(jù)權(quán)限過(guò)濾
本文介紹了MyBatis攔截器的使用,通過(guò)實(shí)現(xiàn)Interceptor接口對(duì)SQL進(jìn)行處理,實(shí)現(xiàn)數(shù)據(jù)權(quán)限過(guò)濾功能,通過(guò)在本地線程變量中存儲(chǔ)數(shù)據(jù)權(quán)限相關(guān)信息,并在攔截器的intercept方法中進(jìn)行SQL增強(qiáng)處理2024-12-12Java 靜態(tài)綁定與動(dòng)態(tài)綁定深入分析
這篇文章主要介紹了Java 靜態(tài)綁定與動(dòng)態(tài)綁定深入分析的相關(guān)資料,這里對(duì)java 的動(dòng)態(tài)綁定和靜態(tài)綁定做了詳細(xì)的介紹,對(duì)其進(jìn)行總結(jié)整理,需要的朋友可以參考下2016-11-11