注冊(cè)個(gè)人網(wǎng)站要多少錢寧波關(guān)鍵詞優(yōu)化時(shí)間
背景:
????????在java多線程當(dāng)中,我們總有遇到過多個(gè)線程操作一個(gè)共享數(shù)據(jù)時(shí),而這個(gè)最后的代碼執(zhí)行結(jié)果并沒有按照我們的預(yù)期一樣得到正確的結(jié)果。此時(shí)我們就需要讓代碼執(zhí)行在操作共享變量時(shí),要等一個(gè)線程操作完畢時(shí),另一個(gè)線程才能去操作這個(gè)共享變量。synchronized鎖就能達(dá)到這樣的目的。在線程A操作某個(gè)共享變量時(shí),其他線程想要操作這個(gè)對(duì)像的話只能先處于等待狀態(tài),只有線程A操作完畢后其他線程才能操作這個(gè)變量。synchronized還有一個(gè)作用,就是將讓其他線程看到這個(gè)線程內(nèi)對(duì)像的變化,獲取到對(duì)像的最新的值。
sychronized鎖的使用方式
????????利用synchronized實(shí)現(xiàn)同步的基礎(chǔ):Java中的每一個(gè)對(duì)象都可以作為鎖。具體表現(xiàn) 為以下3種形式。
- 對(duì)于普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象。
- 對(duì)于靜態(tài)同步方法,鎖是當(dāng)前類的Class對(duì)象。
- 對(duì)于同步方法塊,鎖是Synchonized括號(hào)里配置的對(duì)象。
下面我對(duì)于這三種方式進(jìn)行詳細(xì)的說明,但再此之前要介紹多線程操作共享變量的內(nèi)存圖:
?多線程處理共享數(shù)據(jù)是從主內(nèi)存中將數(shù)據(jù)拷貝一份到自己的工作內(nèi)存當(dāng)中,再工作內(nèi)存當(dāng)中對(duì)其進(jìn)行操作,然后再寫回到主內(nèi)存當(dāng)中。
對(duì)于普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象。
????????當(dāng)sychronized修飾普通方法時(shí),這個(gè)方法已經(jīng)變?yōu)榱送椒椒?#xff0c;并對(duì)這個(gè)對(duì)象加了鎖,此時(shí)想要操作這個(gè)對(duì)像的同步方法要先獲得這個(gè)對(duì)像的鎖,所以說這個(gè)加鎖并不是其他線程都無法訪問這個(gè)對(duì)象了,而是無法訪問這個(gè)對(duì)像內(nèi)被sychronized修飾變?yōu)橥椒椒ǖ姆椒?#xff0c;其他方法并不受影響。
public class ThreadTest {public synchronized void m1() { //同步方法System.out.println("m1方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m1方法結(jié)束");}public synchronized void m2() { //同步方法System.out.println("m2方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m2方法結(jié)束");}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m2();}});t1.start();t2.start();}
}
此時(shí)m1和m2方法都是同步方法,我們創(chuàng)建一個(gè)ThreadTest對(duì)像,然后用兩個(gè)線程分別調(diào)用同一個(gè)對(duì)像的m1和m2方法,根據(jù)前面兩個(gè)所講,可以預(yù)測(cè)由于線程1執(zhí)行方法m1時(shí)就拿到了這個(gè)對(duì)像鎖,則其他線程無法執(zhí)行這個(gè)對(duì)像的其他同步方法。執(zhí)行結(jié)果不出我們所料:
確實(shí)是線程1先執(zhí)行m1,等其執(zhí)行完畢后線程2再執(zhí)行的m2方法。當(dāng)我們將m2變?yōu)槠胀ǚ椒?#xff08;不被關(guān)鍵字synchronized修飾),然后執(zhí)行:
這時(shí)候就沒有等待線程1執(zhí)行同步方法m1完畢后線程2再調(diào)用m2方法。因?yàn)榇藭r(shí)執(zhí)行普通方法m2不需要這個(gè)對(duì)像的鎖,也就不用等待線程A執(zhí)行完釋放鎖了。
對(duì)于靜態(tài)同步方法,鎖是當(dāng)前類的Class對(duì)象。?
????????當(dāng)synchronized修飾的是靜態(tài)方法時(shí),此時(shí)加鎖的并不是這個(gè)類的實(shí)例對(duì)象了,而是這個(gè)類的Class對(duì)象。此時(shí)和前面那種形式有一些不同了。但一個(gè)線程調(diào)用這個(gè)靜態(tài)同步方法時(shí)會(huì)獲取到這個(gè)Class對(duì)像的鎖,其他線程就不能再執(zhí)行這個(gè)類的其他靜態(tài)同步方法。但可以調(diào)用其他普通的靜態(tài)方法和普通方法。同時(shí)也可以調(diào)用它的普通同步方法,因?yàn)檎{(diào)用這兩者方法需要的鎖并不一樣,一個(gè)是類鎖,一個(gè)是對(duì)像鎖,所以他們并不互斥。下面代碼結(jié)果展示一下:
public class ThreadTest {public synchronized void m1() {System.out.println("m1方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m1方法結(jié)束");}public synchronized static void m2() {System.out.println("m2方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m2方法結(jié)束");}public synchronized static void m3() {System.out.println("m3方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m3方法結(jié)束");}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {ThreadTest.m2();}});Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {ThreadTest.m3();}});t1.start();t2.start();t3.start();}
}
我定義了三個(gè)方法都用synchronized修飾了,m1是普通同步方法,而m2、m3是靜態(tài)同步方法,并用三個(gè)線程分別調(diào)用這三個(gè)方法。按照前面原理解釋,m1方法和m2、m3這兩個(gè)方法不互斥,m2和m3這兩個(gè)方法互斥。
由結(jié)果看出,m1和m2方法可以看出不互斥,同時(shí)執(zhí)行,并沒有等待,而m3方法是再m2方法完全執(zhí)行完畢后再執(zhí)行的??梢哉f明m2執(zhí)行時(shí)獲取了Class對(duì)象的鎖,而靜態(tài)同步方法m3想要執(zhí)行的話,就得等待鎖的釋放才能執(zhí)行,最終產(chǎn)生了上面的執(zhí)行結(jié)果。?
對(duì)于同步方法塊,鎖是Synchonized括號(hào)里配置的對(duì)象。
????????鎖住同步方法塊,鎖是synchonized括號(hào)里面那個(gè)引用類型對(duì)像(注意:能作為鎖的只能是引用類型,不能是基本數(shù)據(jù)類型)。這樣子實(shí)現(xiàn)的功能是:因?yàn)榫€程的切換是隨機(jī)的,但我們要保證一段代碼一定要全部執(zhí)行,不想被執(zhí)行到一辦事就被切換到其他線程,無法保證線程安全。此時(shí)就需要synchonized來鎖住這段代碼了。還有就是想要執(zhí)行這個(gè)代碼塊就得先獲取到相對(duì)應(yīng)的對(duì)象鎖。也就是說當(dāng)兩個(gè)線程執(zhí)行兩個(gè)拿同一個(gè)對(duì)象作為鎖的代碼塊,則兩者不能同時(shí)執(zhí)行,必須等一個(gè)代碼塊執(zhí)行完畢,釋放鎖后,另一個(gè)線程才能獲取這個(gè)對(duì)像鎖然后執(zhí)行。
public class ThreadTest {public static Object o = new Object();public void m1() {synchronized(o) {System.out.println("m1方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m1方法結(jié)束");}}public void m2() {synchronized(o){System.out.println("m2方法開始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m2方法結(jié)束");}}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m2();}});t1.start();t2.start();}
}
代碼中兩個(gè)線程各執(zhí)行一個(gè)代碼塊,兩個(gè)代碼塊用的是同一個(gè)對(duì)象鎖,則他們不能同時(shí)執(zhí)行是互斥的。
由輸出結(jié)果可以看出,線程2中的代碼塊是等線程1執(zhí)行完其內(nèi)的代碼塊并釋放鎖后再執(zhí)行的。?