Java中synchronized的四種用法詳解
問(wèn)題描述
介紹之前我們先來(lái)看下,在java 多線(xiàn)程中 如果沒(méi)有線(xiàn)程同步會(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è)線(xiàn)程同時(shí)操作Count變量造成的,如果我們想讓Count有序增加,應(yīng)該給
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;這段代碼同步枷鎖,這樣當(dāng)B線(xiàn)程進(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線(xiàn)程就可以有序的交替執(zhí)行,不會(huì)同時(shí)搶占執(zhí)行Count++ 操作,
下面介紹synchronised的幾種用法
1 鎖方法
public synchronized void dodo(){ }這個(gè)就是鎖方法,這里面要注意兩點(diǎn):
synchronized 關(guān)鍵子不是方法的一部分,所以它不會(huì)被繼承,說(shuō)白了,就是如果父類(lèi)的方法有synchronized,子類(lèi)重寫(xiě)這個(gè)方法,synchronized不寫(xiě)也不會(huì)報(bào)錯(cuò)
synchronized 不能修飾接口
synchronized 不能修復(fù)構(gòu)造方法,但是可以修飾構(gòu)造方法里面的代碼塊
2 鎖代碼塊
鎖代碼塊就是我上面的那個(gè)例子寫(xiě)法了,這個(gè)就是鎖的是某個(gè)對(duì)象中的某個(gè)代碼,讓它線(xiàn)程同步。
synchronized (this) {
System.out.print("ThreadName:"+Thread.currentThread().getName()+" count:"+count+"\n");
count++;
}我們看到這個(gè)synchronized (this) 里面的this,這里是要傳入一個(gè)對(duì)象的,如果不用this也可以,也可以在這個(gè)類(lèi)里面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è)類(lèi)
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è)線(xiàn)程有序執(zhí)行,這個(gè)Count++操作,而且對(duì)run1,和run2都同時(shí)鎖,應(yīng)該怎么辦呢???
答案是鎖類(lèi),鎖類(lèi)的意思是不管是這個(gè)類(lèi)new了多少對(duì)象,這個(gè)對(duì)象的所有方法,都會(huì)上鎖,我們更改下代碼看看結(jié)果,怎么鎖類(lèi)的:
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ì)類(lèi)起作用,因?yàn)槲覀冎纒tatic修飾的方法是類(lèi)方法,所以這個(gè)synchronized 也是作用于整個(gè)類(lèi)
我們把上面的代碼改下看看效果是不是一樣:
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)锳線(xiàn)程 先執(zhí)行最后滿(mǎn)足條件 while (count<15),所以B沒(méi)有機(jī)會(huì)執(zhí)行了,驗(yàn)證符合我們的預(yù)期
總結(jié)
1、鎖如果加在方法上面,或者在方法中的代碼塊形式,就是鎖的這個(gè)對(duì)象,如果鎖是靜態(tài)方法中,或者代碼塊synchronized(A.class) 形式 就是鎖的這個(gè)類(lèi),里面的所有方法都會(huì)同步。
2、誰(shuí)擁有了鎖,上面線(xiàn)程就擁有了控制這段代碼的能力,其他的線(xiàn)程只能看著,只有釋放了鎖,其他線(xiàn)程才可以操作。
3、synchronized 消耗系統(tǒng)性能,所以能不加鎖的邏輯,盡量不要加。
4、操作讀寫(xiě)文件,或者數(shù)據(jù)庫(kù),有的時(shí)候多線(xiàn)程會(huì)出現(xiàn)不可預(yù)知的問(wèn)題,所以要加入鎖。
到此這篇關(guān)于Java中synchronized的四種用法詳解的文章就介紹到這了,更多相關(guān)synchronized的用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用mybatis 實(shí)現(xiàn)量表關(guān)聯(lián)并且統(tǒng)計(jì)數(shù)據(jù)量的步驟和代碼
本文介紹了使用SpringBoot+MyBatis+EasyExcel技術(shù)棧實(shí)現(xiàn)數(shù)據(jù)庫(kù)查詢(xún)結(jié)果導(dǎo)出為Excel文件的步驟,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-10-10
淺談@Aspect@Order各個(gè)通知的執(zhí)行順序
這篇文章主要介紹了@Aspect@Order各個(gè)通知的執(zhí)行順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java 在PPT中添加文本和圖片超鏈接的實(shí)現(xiàn)方法
這篇文章主要介紹了Java 在PPT中添加文本和圖片超鏈接的實(shí)現(xiàn)方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
常用json與javabean互轉(zhuǎn)的方法實(shí)現(xiàn)
這篇文章主要介紹了常用json與javabean互轉(zhuǎn)的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
maven中的maven-antrun-plugin插件示例詳解
maven-antrun-plugin?是?Maven?生態(tài)中一個(gè)強(qiáng)大的工具,尤其適合需要復(fù)用?Ant?腳本或?qū)崿F(xiàn)復(fù)雜構(gòu)建邏輯的場(chǎng)景,然而,過(guò)度使用可能導(dǎo)致構(gòu)建腳本復(fù)雜化,建議權(quán)衡利弊后合理使用,這篇文章主要介紹了maven中的maven-antrun-plugin插件詳解,需要的朋友可以參考下2025-06-06
SpringBoot使用WebSocket實(shí)現(xiàn)向前端推送消息功能
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議,它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動(dòng)發(fā)送信息給客戶(hù)端,本文給大家介紹了SpringBoot使用WebSocket實(shí)現(xiàn)向前端推送消息功能,需要的朋友可以參考下2024-05-05
SpringMVC結(jié)合Jcrop實(shí)現(xiàn)圖片裁剪
這篇文章主要介紹了SpringMVC結(jié)合Jcrop實(shí)現(xiàn)圖片裁剪的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Jenkins一鍵打包部署SpringBoot應(yīng)用
本文主要介紹了Jenkins一鍵打包部署SpringBoot應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01

