java多線(xiàn)程的同步方法實(shí)例代碼
java多線(xiàn)程的同步方法實(shí)例代碼
先看一個(gè)段有關(guān)銀行存錢(qián)的代碼:
class Bank {
private int sum;
public void add(int num){
sum = sum + num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("total num is : " + sum);
}
}
class Custom implements Runnable{
private Bank b = new Bank();
@Override
public void run() {
for(int i = 3 ; i > 0 ; i--)
b.add(100);
}
}
public class BankDemo{
public static void main(String[] args) {
Custom custom = new Custom();
Thread t1 = new Thread(custom);
Thread t2 = new Thread(custom);
t1.start();
t2.start();
}
}
此代碼的運(yùn)行結(jié)果為:
total num is : 100 total num is : 300 total num is : 400 total num is : 500 total num is : 500 total num is : 600
可以看出sum的值與預(yù)期的效果不太一樣;造成這種現(xiàn)象的原因有兩個(gè):
1.程序存在兩個(gè)以上的子線(xiàn)程;
2.子線(xiàn)程中存在多條語(yǔ)句操作同一變量;
上述例子中:創(chuàng)建了兩個(gè)子線(xiàn)程·t1 和 t2,分別向銀行中存錢(qián)。但是可以看出銀行的實(shí)力隨著Custom的創(chuàng)建,只創(chuàng)建了一個(gè)對(duì)象。也就是說(shuō)我們只操作一個(gè)數(shù)據(jù)變量即為銀行中錢(qián)的總數(shù)sum;當(dāng)兩個(gè)子線(xiàn)程開(kāi)啟的時(shí)候run方法中調(diào)用了bank的add方法,而add方法中有兩個(gè)語(yǔ)句都在操作sum一個(gè)sum的增加,一個(gè)是打印sum,當(dāng)兩個(gè)子線(xiàn)程搶占cpu執(zhí)行各自的程序的時(shí)候會(huì)出現(xiàn):
當(dāng)t1執(zhí)行到add以后,t2搶到了cpu的執(zhí)行權(quán),執(zhí)行也是執(zhí)行了add語(yǔ)句,隨后打印出sum的值,這時(shí)候由于sum增加了兩次,所以打印出來(lái)的sum值為200。類(lèi)推,假如這個(gè)時(shí)候t1又搶回了cpu的執(zhí)行權(quán),因此又打印出一次200。
顯然這種現(xiàn)象是我們不希望產(chǎn)生的。我們希望一個(gè)線(xiàn)程存完錢(qián)然后打印出結(jié)果,之后才允許下一次添加操作。這就是多線(xiàn)程會(huì)產(chǎn)生的問(wèn)題,線(xiàn)程不安全。
我們應(yīng)盡量避免這種現(xiàn)象的發(fā)生,Java給我們提供了三種方法來(lái)解決這個(gè)問(wèn)題:
第一種:同步代碼塊
//private Object obj = new Object();
public void add(int num) {
synchronized (this) {
sum = sum + num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("total num is : " + sum);
}
}
將多線(xiàn)程中需要操作同一數(shù)據(jù)對(duì)象的語(yǔ)句使用同步代碼塊包含。同步代碼塊的原理就是:
1.java中每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖;
2.當(dāng)程序運(yùn)行到同步代碼塊的時(shí)候首先會(huì)獲取指定對(duì)象的鎖,這個(gè)鎖對(duì)于多個(gè)線(xiàn)程來(lái)說(shuō)是唯一的。我們可以創(chuàng)建任意一個(gè)對(duì)象(obj)讓他當(dāng)作同步代碼塊的鎖。
3.當(dāng)程序中只有一個(gè)只有一個(gè)鎖的話(huà)我們還可以使用this,this代表當(dāng)前執(zhí)行代碼所操作的實(shí)例對(duì)象的鎖。即擁有add方法的類(lèi)的對(duì)象,即bank。
4.兩個(gè)并發(fā)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)對(duì)象object中的這個(gè)synchronized(this)同步代碼塊時(shí),一個(gè)時(shí)間內(nèi)只能有一個(gè)線(xiàn)程得到執(zhí)行。另一個(gè)線(xiàn)程必須等待當(dāng)前線(xiàn)程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。
這樣就可以操作同一個(gè)數(shù)據(jù)的多條語(yǔ)句只能在“同一段時(shí)間”只能被一個(gè)子線(xiàn)程所操作。
第二種 同步函數(shù)
public synchronized void add(int num) {
sum = sum + num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("total num is : " + sum);
}
除了同步代碼塊以外我們還可以將需要同步的操作抽象成一個(gè)函數(shù),然后將這個(gè)函數(shù)用synchronized修飾,形成同步方法。比如上述例子中的add方法中的語(yǔ)句都在操作sum對(duì)象。我們就可以將add方法使用synchronized修飾。這樣也能達(dá)到代碼同步的效果。
同步方法使用的鎖其實(shí)就是 this。
值得一提的是:同步方法和同步代碼塊,在開(kāi)發(fā)程序的時(shí)候我們更推薦使用同步代碼塊。
1.同步代碼塊可以綁定任意對(duì)象,而同步函數(shù)只能綁定該類(lèi)對(duì)象this
2.如果多個(gè)線(xiàn)程使用同一個(gè)鎖的話(huà),那么兩者均可以使用,如果存在多個(gè)鎖的(比如,在一個(gè)對(duì)象的同步方法里面調(diào)用另外一個(gè)對(duì)象的同步方法,則獲取了兩個(gè)對(duì)象的同步鎖),只能使用同步代碼塊。
靜態(tài)方法的同步
同步方法
public synchronized static void add(int num){}
同步代碼塊:
public synchronized void add(int num){
synchronized (Bank.Class) {
}
}
靜態(tài)方法的默認(rèn)同步鎖是當(dāng)前方法所在類(lèi)的.class 對(duì)象,注意this與static不可以連用,所以不能使用this.Class
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Java利用StampedLock實(shí)現(xiàn)讀寫(xiě)鎖的方法詳解
在jdk8以后,java提供了一個(gè)性能更優(yōu)越的讀寫(xiě)鎖并發(fā)類(lèi)StampedLock,該類(lèi)的設(shè)計(jì)初衷是作為一個(gè)內(nèi)部工具類(lèi),用于輔助開(kāi)發(fā)其它線(xiàn)程安全組件。本文就來(lái)和大家一起學(xué)習(xí)下StampedLock的功能和使用2022-10-10
java實(shí)現(xiàn)文件和base64相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)文件和base64相互轉(zhuǎn)換,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11
SpringBoot+SpringSecurity實(shí)現(xiàn)認(rèn)證的流程詳解
這篇文章主要介紹了SpringBoot+SpringSecurity實(shí)現(xiàn)認(rèn)證的流程,文中通過(guò)代碼示例和圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05
Java實(shí)現(xiàn)簡(jiǎn)單推箱子游戲
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)推箱子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
SpringBoot @CompentScan excludeFilters配置無(wú)效的解決方案
這篇文章主要介紹了SpringBoot @CompentScan excludeFilters配置無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
RocketMQ消息過(guò)濾與查詢(xún)的實(shí)現(xiàn)
這篇文章主要介紹了RocketMQ消息過(guò)濾與查詢(xún)的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Mybatis實(shí)體類(lèi)屬性與數(shù)據(jù)庫(kù)不一致解決方案
這篇文章主要介紹了Mybatis實(shí)體類(lèi)屬性與數(shù)據(jù)庫(kù)不一致解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

