淺談同步監(jiān)視器之同步代碼塊、同步方法
如果有多個(gè)線程訪問(wèn)共享資源,可能會(huì)出現(xiàn)當(dāng)一個(gè)線程沒(méi)有處理完業(yè)務(wù),然后另一個(gè)線程進(jìn)入,從而導(dǎo)致共享資源出現(xiàn)不安全的情況。
日常例子:銀行取錢,A和B有擁有同一個(gè)銀行賬戶,A用存折在柜臺(tái)取錢,B在取款機(jī)取錢。取錢有兩個(gè)關(guān)鍵步驟:
(1)判斷賬戶里的錢的余額是否大于所取錢數(shù)
(2)如果大于所取錢數(shù),則賬戶最終所剩余額 = 余額 - 所取錢數(shù)。
如果沒(méi)有線程同步的情況下,我們假設(shè)這一種情況,這個(gè)共同的賬戶里共1000元。
(1)A B同時(shí)去取600元,A所在線程執(zhí)行到上面的第一個(gè)步驟,判斷所取錢數(shù)小于現(xiàn)有余額,CPU時(shí)間片用完。
(2)這時(shí)B進(jìn)來(lái)到第一個(gè)步驟,同樣是執(zhí)行判斷,因?yàn)锳只執(zhí)行完第一步驟,沒(méi)有執(zhí)行減法,這時(shí)現(xiàn)有余額還是1000元。
(3)由于在CPU分配的時(shí)間里他接著完成了減法操作。這時(shí)賬戶余額為1000 - 600 = 400。成功取出600元。
(4)最后A接著之前執(zhí)行的步驟,去做減法操作, 賬戶余額為 -200 = 400 - 600。
到這里,我只想說(shuō)為什么,是什么銀行可以允許你這么做, 當(dāng)然,除非銀行是你家開的。
總之銀行不可能讓這種情況發(fā)生,所以我們的偉大先賢們就想到線程同步,其實(shí)很簡(jiǎn)單,你也能想到。如果讓這兩個(gè)步驟同時(shí)完成,不可分開,問(wèn)題也就迎刃而解。
下面就說(shuō)到在JAVA中同步代碼的實(shí)現(xiàn):
涉及概念:同步監(jiān)視器,是一個(gè)普通的java對(duì)象,同一個(gè)同步監(jiān)視器如果一個(gè)線程拿到,則其他線程就沒(méi)有辦法拿到。好像是一個(gè)房門里只有唯一的一把鑰匙, 不能復(fù)制。如果一個(gè)人拿著它進(jìn)入房門,其他人只能在外面等候。等他出來(lái)你獲得了它,你才能進(jìn)入房間。
下面的代碼如果沒(méi)有做線程同步操作(同步代碼塊、同步方法、同步鎖)結(jié)果是如下:
Thread-1------判斷所取錢數(shù)是否大于余額------
Thread-0------判斷所取錢數(shù)是否大于余額------
Thread-0======做減法操作,取出現(xiàn)金======
Thread-1======做減法操作,取出現(xiàn)金======
很顯然線程1的那兩步?jīng)]有同時(shí)完成。
下面的幾種方法可以實(shí)現(xiàn)兩步同時(shí)完成。
1、同步代碼塊:
public class ThreadTest {
public static void main(String[] args){
Thread t1 = new Thread1(); //線程1
Thread t2 = new Thread1();//線程2
t1.start();
t2.start();
}
}
class Thread1 extends Thread{
@Override
public void run() {
super.run();
try {
BeTested b = new BeTested(); // 這地方,因?yàn)檫@個(gè)例子中同步監(jiān)視器 obj 是線程共享的,兩個(gè)線程用兩個(gè)不同的對(duì)象,也沒(méi)有關(guān)系,不影響結(jié)果。
b.beTested(this);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class BeTested {
static Object obj = new Object();;
public void beTested(Thread t) throws InterruptedException{
synchronized (obj) { // obj 為同步監(jiān)視器
System.out.println(t.getName() + "------判斷所取錢數(shù)是否大于余額------");
t.sleep(1000); // 如果沒(méi)有同步這樣能理明顯地看到這兩步驟不能在一個(gè)線程,同一個(gè)時(shí)間片里執(zhí)行完成。
System.out.println(t.getName() + "======做減法操作,取出現(xiàn)金======");
}
}
}
執(zhí)行結(jié)果如下:
Thread-0------判斷所取錢數(shù)是否大于余額------
Thread-0======做減法操作,取出現(xiàn)金======
Thread-1------判斷所取錢數(shù)是否大于余額------
Thread-1======做減法操作,取出現(xiàn)金======
注意:同步監(jiān)視器對(duì)象的選用很關(guān)鍵。要選擇線程共享的對(duì)象,比如上面例子的 obj, 它是static修飾的才行,如果沒(méi)有static修飾,則是使用不同的同步監(jiān)視器(不是同一個(gè)對(duì)象),相當(dāng)于是兩把鑰匙。
?。ㄈ绻鹢bj = "aaaa" 沒(méi)有static修飾也可以實(shí)現(xiàn)同步,那是因?yàn)檫@個(gè)obj引用的常量池里的同一個(gè)string對(duì)象,強(qiáng)烈不推薦使用)
2、同步方法(非靜態(tài)方法)
把上面的那兩類改成如下,main方法所在類不變。
class Thread1 extends Thread{
static BeTested b = new BeTested(); // 在這種方法中,這里必須是同個(gè)對(duì)象(static修飾),下文會(huì)詳細(xì)說(shuō)明
@Override
public void run() {
super.run();
try {
b.beTested(this);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class BeTested {
static Object obj = new Object();;
public synchronized void beTested(Thread t) throws InterruptedException{
System.out.println(t.getName() + "------判斷所取錢數(shù)是否大于余額------");
t.sleep(1000);
System.out.println(t.getName() + "======做減法操作,取出現(xiàn)金======");
}
}
執(zhí)行結(jié)果如下:
Thread-0------判斷所取錢數(shù)是否大于余額------
Thread-0======做減法操作,取出現(xiàn)金======
Thread-1------判斷所取錢數(shù)是否大于余額------
Thread-1======做減法操作,取出現(xiàn)金======
注意:因?yàn)橥椒椒ㄖ?,所用的同步監(jiān)視器不能指定,默認(rèn)使用的調(diào)用該方法的對(duì)象,也就是this。所以 Thread1 類中相對(duì)于示例1中同步代碼塊中修改的部分, 也是要static修飾。也就是說(shuō)要使用同一個(gè)對(duì)象。
3、同步方法(靜態(tài)方法)
把上面的那兩類改成如下,main方法所在類不變。
class Thread1 extends Thread{
@Override
public void run() {
super.run();
try {
BeTested b = new BeTested(); // 這里每個(gè)線程使用不同的對(duì)象。
b.beTested(this);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class BeTested {
static Object obj = new Object();;
public static synchronized void beTested(Thread t) throws InterruptedException{
System.out.println(t.getName() + "------判斷所取錢數(shù)是否大于余額------");
t.sleep(1000);
System.out.println(t.getName() + "======做減法操作,取出現(xiàn)金======");
}
}
執(zhí)行結(jié)果如下:
Thread-0------判斷所取錢數(shù)是否大于余額------
Thread-0======做減法操作,取出現(xiàn)金======
Thread-1------判斷所取錢數(shù)是否大于余額------
Thread-1======做減法操作,取出現(xiàn)金======
注意:因?yàn)橥届o態(tài)方法中,同步監(jiān)視器是這個(gè)類而不是這個(gè)類的對(duì)象。所以Thread1 類中相對(duì)于示例2中同步代碼塊中修改的部分,不須要用static修飾,不是同一個(gè)對(duì)象也沒(méi)關(guān)系。因?yàn)檫@個(gè)類他本身就是共享的。
總結(jié):如上幾種方式進(jìn)行線程同步處理時(shí),要注意你所使用的同步監(jiān)視器對(duì)象,它必須是共享的。
注:還有使用同步鎖的方式實(shí)現(xiàn)線程同步,本篇文章不做討論。
以上這篇淺談同步監(jiān)視器之同步代碼塊、同步方法就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決idea spring boot 修改html等不重啟即時(shí)生效的問(wèn)題
這篇文章主要介紹了解決idea spring boot 修改html等不重啟即時(shí)生效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Apache Shrio安全框架實(shí)現(xiàn)原理及實(shí)例詳解
這篇文章主要介紹了Apache Shrio安全框架實(shí)現(xiàn)原理及實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
詳解spring boot jpa整合QueryDSL來(lái)簡(jiǎn)化復(fù)雜操作
這篇文章主要介紹了詳解spring boot jpa整合QueryDSL來(lái)簡(jiǎn)化復(fù)雜操作,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Java 獲取Web項(xiàng)目相對(duì)webapp地址的實(shí)例
下面小編就為大家?guī)?lái)一篇Java 獲取Web項(xiàng)目相對(duì)webapp地址的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
java實(shí)現(xiàn)拉鉤網(wǎng)上的FizzBuzzWhizz問(wèn)題示例
這篇文章主要介紹了java實(shí)現(xiàn)拉鉤網(wǎng)上的FizzBuzzWhizz問(wèn)題示例,需要的朋友可以參考下2014-05-05
淺談Java?abstract關(guān)鍵字不能和哪些關(guān)鍵字共存
本文主要介紹了Java?abstract關(guān)鍵字不能和哪些關(guān)鍵字共存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-10-10
詳解springMVC兩種方式實(shí)現(xiàn)多文件上傳及效率比較
本篇文章介紹了springMVC兩種方式實(shí)現(xiàn)多文件上傳及效率比較。springMVC實(shí)現(xiàn)多文件上傳有兩種,一種是字節(jié)流的方式進(jìn)行文件上傳,另外一種是使用springMVC包裝好的解析器進(jìn)行上傳,有興趣的可以了解一下。2016-12-12
Maven多模塊及version修改的實(shí)現(xiàn)方法
這篇文章主要介紹了Maven多模塊及version修改的實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
ScheduledExecutorService任務(wù)定時(shí)代碼示例
這篇文章主要介紹了ScheduledExecutorService任務(wù)定時(shí)代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01
java開發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用
這篇文章主要為大家介紹了java開發(fā)ServiceLoader實(shí)現(xiàn)機(jī)制及SPI應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

