Java使用wait() notify()方法操作共享資源詳解
Java多個(gè)線程共享資源;
1)wait()、notify()和notifyAll()方法是本地方法,并且為final方法,無(wú)法被重寫。
2)調(diào)用某個(gè)對(duì)象的wait()方法能讓當(dāng)前線程阻塞,并且當(dāng)前線程必須擁有此對(duì)象的monitor(即鎖,或者叫管程)
3)調(diào)用某個(gè)對(duì)象的notify()方法能夠喚醒一個(gè)正在等待這個(gè)對(duì)象的monitor的線程,如果有多個(gè)線程都在等待這個(gè)對(duì)象的monitor,則只能喚醒其中一個(gè)線程;
4)調(diào)用notifyAll()方法能夠喚醒所有正在等待這個(gè)對(duì)象的monitor的線程;
在Java中,是沒(méi)有類似于PV操作、進(jìn)程互斥等相關(guān)的方法的。JAVA的進(jìn)程同步是通過(guò)synchronized()來(lái)實(shí)現(xiàn)的,需要說(shuō)明的是,Java的synchronized()方法類似于操作系統(tǒng)概念中的互斥內(nèi)存塊,在Java中的Object類對(duì)象中,都是帶有一個(gè)內(nèi)存鎖的,在有線程獲取該內(nèi)存鎖后,其它線程無(wú)法訪問(wèn)該內(nèi)存,從而實(shí)現(xiàn)Java中簡(jiǎn)單的同步、互斥操作。明白這個(gè)原理,就能理解為什么synchronized(this)與synchronized(static XXX)的區(qū)別了,synchronized就是針對(duì)內(nèi)存區(qū)塊申請(qǐng)內(nèi)存鎖,this關(guān)鍵字代表類的一個(gè)對(duì)象,所以其內(nèi)存鎖是針對(duì)相同對(duì)象的互斥操作,而static成員屬于類專有,其內(nèi)存空間為該類所有成員共有,這就導(dǎo)致synchronized()對(duì)static成員加鎖,相當(dāng)于對(duì)類加鎖,也就是在該類的所有成員間實(shí)現(xiàn)互斥,在同一時(shí)間只有一個(gè)線程可訪問(wèn)該類的實(shí)例。如果需要在線程間相互喚醒就需要借助Object類的wait()方法及nofity()方法。
說(shuō)了這么一堆,可能似懂非懂,那么接下來(lái)用一個(gè)例子來(lái)說(shuō)明問(wèn)題,用多線程實(shí)現(xiàn)連續(xù)的1,2,1,2,1,2,1,2,1,2輸出。
package com.study.thread;
/**
* 多線程
* @ClassName: PrintFile
* @date 2017年10月10日 下午4:05:04
*/
public class PrintFile implements Runnable{
//當(dāng)前線程id
private int id ;
//共享資源
public byte[] res ;
//如果類里寫了有參構(gòu)造器,而任然想保留無(wú)參數(shù)構(gòu)造方法,則必須顯式的寫出該方法。
public PrintFile() {
super();
// System.out.println("我是構(gòu)造器");
}
public PrintFile(int id, byte[] res) {
//構(gòu)造器中使用super()/this(),必須放在第一行。
this();
this.id = id;
this.res = res;
}
//靜態(tài)計(jì)數(shù)器
public static int count = 5;
@Override
public void run() {
synchronized (res) {
while(count-->=0){
try {
res.notify();//喚醒其他線程中的某一個(gè)(喚醒等待res的其他線程,當(dāng)前線程執(zhí)行完后要釋放鎖)
System.out.println("當(dāng)前線程id值:"+id);
res.wait();//當(dāng)前線程阻塞,等待被喚醒
System.out.println("現(xiàn)在執(zhí)行的線程是"+Thread.currentThread().getName()+",--wait()后的代碼繼續(xù)執(zhí)行:"+id);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return;
}
}
}
測(cè)試:
package com.study.thread;
public class PrintFileTest {
public static void main(String[] args) {
byte[] res = new byte[]{0,1,2};//共享資源
PrintFile p1 = new PrintFile(1, res);
PrintFile p2 = new PrintFile(2, res);
Thread t1 = new Thread(p1, "a");
Thread t2 = new Thread(p2, "b");
t1.start();
t2.start();
}
}
結(jié)果:
當(dāng)前線程id值:1
當(dāng)前線程id值:2
現(xiàn)在執(zhí)行的線程是a,--wait()后的代碼繼續(xù)執(zhí)行:1
當(dāng)前線程id值:1
現(xiàn)在執(zhí)行的線程是b,--wait()后的代碼繼續(xù)執(zhí)行:2
當(dāng)前線程id值:2
現(xiàn)在執(zhí)行的線程是a,--wait()后的代碼繼續(xù)執(zhí)行:1
當(dāng)前線程id值:1
現(xiàn)在執(zhí)行的線程是b,--wait()后的代碼繼續(xù)執(zhí)行:2
當(dāng)前線程id值:2
現(xiàn)在執(zhí)行的線程是a,--wait()后的代碼繼續(xù)執(zhí)行:1
下面解釋為什么會(huì)出現(xiàn)這樣的結(jié)果:
首先1、2號(hào)線程啟動(dòng),這里假設(shè)1號(hào)線程先運(yùn)行run方法獲得資源(實(shí)際上是不確定的),獲得對(duì)象a的鎖,進(jìn)入while循環(huán)(用于控制輸出幾輪):
1、此時(shí)對(duì)象調(diào)用它的喚醒方法notify(),意思是這個(gè)同步塊執(zhí)行完后它要釋放鎖,把鎖交給等待a資源的線程;
2、輸出1;
3、該對(duì)象執(zhí)行等待方法,意思是此時(shí)此刻起擁有這個(gè)對(duì)象鎖的線程(也就是這里的1號(hào)線程)釋放CPU控制權(quán),釋放鎖,并且線程進(jìn)入阻塞狀態(tài),后面的代碼暫時(shí)不執(zhí)行,因未執(zhí)行完同步塊,所以1也沒(méi)起作用;
4、在這之前的某時(shí)刻線程2運(yùn)行run方法,但苦于沒(méi)有獲得a對(duì)象的鎖,所以無(wú)法繼續(xù)運(yùn)行,但3步驟之后,它獲得了a的鎖,此時(shí)執(zhí)行a的喚醒方法notify(),同理,意思是這個(gè)同步塊執(zhí)行完后它要釋放鎖,把鎖交給等待a資源的線程;
5、輸出2;
6、執(zhí)行a的等待方法,意思是此時(shí)此刻起擁有這個(gè)對(duì)象鎖的線程(也就是這里的2號(hào)線程)釋放CPU控制權(quán),釋放鎖,并且線程進(jìn)入阻塞狀態(tài),后面的代碼暫時(shí)不執(zhí)行,因未執(zhí)行完同步塊,所以2號(hào)線程的4步驟的喚醒方法也沒(méi)起作用;
7、此時(shí)1號(hào)線程執(zhí)行到3步驟,發(fā)現(xiàn)對(duì)象鎖沒(méi)有被使用,所以繼續(xù)執(zhí)行3步驟中wait方法后面的代碼,于是輸出:------線程1獲得鎖,wait()后的代碼繼續(xù)運(yùn)行:1;
8、此時(shí)while循環(huán)滿足條件,繼續(xù)執(zhí)行,所以,再執(zhí)行1號(hào)線程的喚醒方法,意思是這個(gè)同步塊執(zhí)行完后它要釋放鎖;
9、輸出1;
10、執(zhí)行等待方法,線程1阻塞,釋放資源鎖;
11、此時(shí)線程2又獲得了鎖,執(zhí)行到步驟6,繼續(xù)執(zhí)行wait方法后面的代碼,所以輸出:------線程2獲得鎖,wait()后的代碼繼續(xù)運(yùn)行:2;
12、繼續(xù)執(zhí)行while循環(huán),輸出2;
··· ···
通過(guò)上述步驟,相信大家已經(jīng)明白這兩個(gè)方法的使用了,但該程序還存在一個(gè)問(wèn)題,當(dāng)while循環(huán)不滿足條件時(shí),肯定會(huì)有線程還在等待資源,所以主線程一直不會(huì)終止。當(dāng)然這個(gè)程序的目的僅僅為了給大家演示這兩個(gè)方法怎么用。
總結(jié):
wait()方法與notify()必須要與synchronized(resource)一起使用。也就是wait與notify針對(duì)已經(jīng)獲取了resource鎖的線程進(jìn)行操作,從語(yǔ)法角度來(lái)說(shuō)就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語(yǔ)句塊內(nèi)。從功能上來(lái)說(shuō)wait()線程在獲取對(duì)象鎖后,主動(dòng)釋放CPU控制權(quán),主動(dòng)釋放對(duì)象鎖,同時(shí)本線程休眠。直到有其它線程調(diào)用對(duì)象的notify()喚醒該線程,才能繼續(xù)獲取對(duì)象鎖,并繼續(xù)執(zhí)行。相應(yīng)的notify()就是對(duì)對(duì)象鎖的釋放操作。【因此,我們可以發(fā)現(xiàn),wait和notify方法均可釋放對(duì)象的鎖,但wait同時(shí)釋放CPU控制權(quán),即它后面的代碼停止執(zhí)行,線程進(jìn)入阻塞狀態(tài),而notify方法不立刻釋放CPU控制權(quán),而是在相應(yīng)的synchronized(){}語(yǔ)句塊執(zhí)行結(jié)束,再自動(dòng)釋放鎖?!酷尫沛i后,JVM會(huì)在等待resoure的線程中選取一線程,賦予其對(duì)象鎖,喚醒線程,繼續(xù)執(zhí)行。這樣就提供了在線程間同步、喚醒的操作。Thread.sleep()與Object.wait()二者都可以暫停當(dāng)前線程,釋放CPU控制權(quán),主要的區(qū)別在于Object.wait()在釋放CPU同時(shí),釋放了對(duì)象鎖的控制,而在同步塊中的Thread.sleep()方法并不釋放鎖,僅釋放CPU控制權(quán)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java多線程之wait(),notify(),notifyAll()的詳解分析
- Java的wait(), notify()和notifyAll()使用心得
- Java 中Object的wait() notify() notifyAll()方法使用
- Java多線程通信wait()和notify()代碼實(shí)例
- java wait()/notify() 實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式詳解
- Java多線程wait()和notify()方法詳細(xì)圖解
- java使用wait()和notify()線程間通訊的實(shí)現(xiàn)
- JAVA中wait()和notify()如何使用詳解
相關(guān)文章
SpringBoot利用自定義注解實(shí)現(xiàn)隱私數(shù)據(jù)脫敏(加密顯示)的解決方案
這兩天在整改等保測(cè)出的問(wèn)題,里面有一個(gè)“用戶信息泄露”的風(fēng)險(xiǎn)項(xiàng)(就是后臺(tái)系統(tǒng)里用戶的一些隱私數(shù)據(jù)直接明文顯示了),其實(shí)指的就是要做數(shù)據(jù)脫敏,本文給大家介紹了SpringBoot利用自定義注解實(shí)現(xiàn)隱私數(shù)據(jù)脫敏(加密顯示)的解決方案,需要的朋友可以參考下2023-11-11
淺談BeanPostProcessor加載次序及其對(duì)Bean造成的影響分析
這篇文章主要介紹了淺談BeanPostProcessor加載次序及其對(duì)Bean造成的影響分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
如何解決Maven下載的依賴版本和引入的依賴版本不一致問(wèn)題
這篇文章主要介紹了如何解決Maven下載的依賴版本和引入的依賴版本不一致問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
JAVA實(shí)現(xiàn)簡(jiǎn)單停車場(chǎng)系統(tǒng)代碼
JAVA項(xiàng)目中正號(hào)需要一個(gè)停車收費(fèi)系統(tǒng),就整理出來(lái)java實(shí)現(xiàn)的一個(gè)簡(jiǎn)單的停車收費(fèi)系統(tǒng)給大家分享一下,希望對(duì)大家有所幫助2017-04-04
mybatis 使用jdbc.properties文件設(shè)置不起作用的解決方法
這篇文章主要介紹了mybatis 使用jdbc.properties文件設(shè)置不起作用的解決方法,需要的朋友可以參考下2018-03-03
springboot?vue接口測(cè)試HutoolUtil?TreeUtil處理樹(shù)形結(jié)構(gòu)
這篇文章主要介紹了springboot?vue接口測(cè)試HutoolUtil?TreeUtil處理樹(shù)形結(jié)構(gòu),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05

