日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java 线程不足_Java 线程基础知识

發(fā)布時(shí)間:2023/12/10 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 线程不足_Java 线程基础知识 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

wait() 和notify()、notifyAll()

這三個(gè)方法用于協(xié)調(diào)多個(gè)線(xiàn)程對(duì)共享數(shù)據(jù)的存取,所以必須在 Synchronized 語(yǔ)句塊內(nèi)使用這三個(gè)方法,否則會(huì)拋出錯(cuò) IllegalMonitorStateException。前面說(shuō)過(guò) Synchronized 這個(gè)關(guān)鍵字用于保護(hù)共享數(shù)據(jù),阻止其他線(xiàn)程對(duì)共享數(shù)據(jù)的存取。但是這樣程序的流程就很不靈活了,如何才能在當(dāng)前線(xiàn)程還沒(méi)退出 Synchronized 數(shù)據(jù)塊時(shí)讓其他線(xiàn)程也有機(jī)會(huì)訪(fǎng)問(wèn)共享數(shù)據(jù)呢?此時(shí)就用這三個(gè)方法來(lái)靈活控制。

wait() 方法使當(dāng)前線(xiàn)程被阻塞掛起暫停執(zhí)行并釋放對(duì)象鎖標(biāo)志,讓其他線(xiàn)程可以進(jìn)入 Synchronized 數(shù)據(jù)塊,當(dāng)前線(xiàn)程被放入對(duì)象等待池中。當(dāng)調(diào)用共享對(duì)象的 notify() 或者 notifyAll() 方法才會(huì)返回,此時(shí)返回的線(xiàn)程會(huì)加入到鎖標(biāo)志等待池中,只有鎖標(biāo)志等待池中的線(xiàn)程能夠獲取鎖標(biāo)志,如果鎖標(biāo)志等待池中沒(méi)有線(xiàn)程,則 notify() 不起作用。

notifyAll() 則從對(duì)象等待池中移走所有等待那個(gè)對(duì)象的線(xiàn)程并放到鎖標(biāo)志等待池中。

需要注意的是,當(dāng)線(xiàn)程調(diào)用共享對(duì)象的 wait() 方法時(shí),當(dāng)前線(xiàn)程只會(huì)釋放當(dāng)前共享對(duì)象的鎖,當(dāng)前線(xiàn)程持有的其他共享對(duì)象的監(jiān)視器鎖并不會(huì)釋放。

線(xiàn)程中斷

為啥需要中斷呢?下面簡(jiǎn)單的舉例情況:

比如我們會(huì)啟動(dòng)多個(gè)線(xiàn)程做同一件事,比如搶 12306 的火車(chē)票,我們可能開(kāi)啟多個(gè)線(xiàn)程從多個(gè)渠道買(mǎi)火車(chē)票,只要有一個(gè)渠道買(mǎi)到了,我們會(huì)通知取消其他渠道。這個(gè)時(shí)候需要關(guān)閉其他線(xiàn)程;

很多線(xiàn)程的運(yùn)行模式是死循環(huán),比如在生產(chǎn)者/消費(fèi)者模式中,消費(fèi)者主體就是一個(gè)死循環(huán),它不停的從隊(duì)列中接受任務(wù),執(zhí)行任務(wù),在停止程序時(shí),我們需要一種”優(yōu)雅”的方法以關(guān)閉該線(xiàn)程;

在一些場(chǎng)景中,比如從第三方服務(wù)器查詢(xún)一個(gè)結(jié)果,我們希望在限定的時(shí)間內(nèi)得到結(jié)果,如果得不到,我們會(huì)希望取消該任務(wù);

上面這幾個(gè)例子線(xiàn)程已經(jīng)在運(yùn)行了,并不好去干涉,但是可以通過(guò)中斷,告訴這個(gè)線(xiàn)程,你應(yīng)該中斷了。比如上面的例子中的線(xiàn)程再收到中斷后,可以通過(guò)中斷標(biāo)志來(lái)結(jié)束線(xiàn)程的運(yùn)行。當(dāng)然,你也可以收到后,不做任何處理,這也是可以的。

在 Java 中,停止一個(gè)線(xiàn)程的主要機(jī)制是中斷,中斷并不是強(qiáng)迫終止一個(gè)線(xiàn)程,它是一種協(xié)作機(jī)制,是給線(xiàn)程傳遞一個(gè)取消信號(hào),但是由線(xiàn)程來(lái)決定如何以及何時(shí)退出。

需要注意的是:在停止線(xiàn)程的時(shí)候,不要調(diào)用 stop 方法,該方法已經(jīng)被廢棄了,并且會(huì)帶來(lái)不可預(yù)測(cè)的影響。

線(xiàn)程對(duì)中斷的反應(yīng)

RUNNABLE:線(xiàn)程在運(yùn)行或具備運(yùn)行條件只是在等待操作系統(tǒng)調(diào)度

WAITING/TIMED_WAITING:線(xiàn)程在等待某個(gè)條件或超時(shí)

BLOCKED:線(xiàn)程在等待鎖,試圖進(jìn)入同步塊

NEW/TERMINATED:線(xiàn)程還未啟動(dòng)或已結(jié)束

線(xiàn)程中斷常用的方法

interrupt() :中斷線(xiàn)程,將會(huì)設(shè)置該線(xiàn)程的中斷狀態(tài)位,即設(shè)置為true,中斷的結(jié)果線(xiàn)程是死亡、還是等待新的任務(wù)或是繼續(xù)運(yùn)行至下一步,就取決于這個(gè)程序本身。線(xiàn)程會(huì)不時(shí)地檢測(cè)這個(gè)中斷標(biāo)示位,以判斷線(xiàn)程是否應(yīng)該被中斷(中斷標(biāo)示值是否為true)。它并不像stop方法那樣會(huì)中斷一個(gè)正在運(yùn)行的線(xiàn)程。

interrupted():第一次使用返回true,并清除中斷標(biāo)志位,在此之后查詢(xún)中斷狀態(tài)isInterrupt()都會(huì)返回false,剛剛第一個(gè)例子也看到了,利用? ? 第一次返回的true可以跳出循環(huán)。第二次以及以后都是返回false。

isInterrupted():僅僅查詢(xún)中斷標(biāo)志位來(lái)判斷是否發(fā)生中斷并返回true或者false。

RUNNABLE 狀態(tài)

如果線(xiàn)程在運(yùn)行中,interrupt() 只是會(huì)設(shè)置線(xiàn)程的中斷標(biāo)志位,沒(méi)有任何其它作用。線(xiàn)程應(yīng)該在運(yùn)行過(guò)程中合適的位置檢查中斷標(biāo)志位,比如說(shuō),如果主體代碼是一個(gè)循環(huán),可以在循環(huán)開(kāi)始處進(jìn)行檢查,如下所示:

public class InterruptRunnableDemo extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) { // 也可以使用 !Thread.currentThread().interrupted() 來(lái)判斷有沒(méi)有中斷//... 單次循環(huán)代碼

}

System.out.println("done ");

}public static void main(String[] args) throwsInterruptedException {

Thread thread= newInterruptRunnableDemo();

thread.start();

Thread.sleep(1000);

thread.interrupt();

}

}

WAITING/TIMED_WAITING

線(xiàn)程執(zhí)行如下方法會(huì)進(jìn)入WAITING狀態(tài):

public final void join() throwsInterruptedExceptionpublic final void wait() throws InterruptedException

執(zhí)行如下方法會(huì)進(jìn)入TIMED_WAITING狀態(tài):

public final native void wait(long timeout) throwsInterruptedException;public static native void sleep(long millis) throwsInterruptedException;public final synchronized void join(long millis) throws InterruptedException

在這些狀態(tài)時(shí),對(duì)線(xiàn)程對(duì)象調(diào)用 interrupt() 會(huì)使得該線(xiàn)程拋出 InterruptedException,需要注意的是,拋出異常后,中斷標(biāo)志位會(huì)被清空(線(xiàn)程的中斷標(biāo)志位會(huì)由 true 重置為false,因?yàn)榫€(xiàn)程為了處理異常已經(jīng)重新處于就緒狀態(tài)),而不是被設(shè)置。比如說(shuō),執(zhí)行如下代碼:

Thread t = newThread (){

@Overridepublic voidrun() {try{

Thread.sleep(1000);

}catch(InterruptedException e) {//exception被捕獲,但是為輸出為false 因?yàn)闃?biāo)志位會(huì)被清空

System.out.println(isInterrupted());

}

}

};

t.start();try{

Thread.sleep(100);

}catch(InterruptedException e) {

}

t.interrupt();//置為true

InterruptedException 是一個(gè)受檢異常,線(xiàn)程必須進(jìn)行處理。我們?cè)诋惓L幚碇薪榻B過(guò),處理異常的基本思路是,如果你知道怎么處理,就進(jìn)行處理,如果不知道,就應(yīng)該向上傳遞,通常情況下,你不應(yīng)該做的是,捕獲異常然后忽略。

捕獲到 InterruptedException,通常表示希望結(jié)束該線(xiàn)程,線(xiàn)程大概有兩種處理方式:

向上傳遞該異常,這使得該方法也變成了一個(gè)可中斷的方法,需要調(diào)用者進(jìn)行處理

有些情況,不能向上傳遞異常,比如Thread的run方法,它的聲明是固定的,不能拋出任何受檢異常,這時(shí),應(yīng)該捕獲異常,進(jìn)行合適的清理操作,清理后,一般應(yīng)該調(diào)用Thread的interrupt方法設(shè)置中斷標(biāo)志位,使得其他代碼有辦法知道它發(fā)生了中斷

第一種方式的示例代碼如下:

//拋出中斷異常,由調(diào)用者捕獲

public void interruptibleMethod() throwsInterruptedException{//... 包含wait, join 或 sleep 方法

Thread.sleep(1000);

}

第二種方式的示例代碼如下:

public class InterruptWaitingDemo extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) {try{//模擬任務(wù)代碼

Thread.sleep(2000);

}catch(InterruptedException e) {//... 清理操作

System.out.println(isInterrupted());//false//重設(shè)中斷標(biāo)志位為true

Thread.currentThread().interrupt();

}

}

System.out.println(isInterrupted());//true

}public static voidmain(String[] args) {

InterruptWaitingDemo thread= newInterruptWaitingDemo();

thread.start();try{

Thread.sleep(100);

}catch(InterruptedException e) {

}

thread.interrupt();

}

}

BLOCKED

如果線(xiàn)程在等待鎖,對(duì)線(xiàn)程對(duì)象調(diào)用interrupt()只是會(huì)設(shè)置線(xiàn)程的中斷標(biāo)志位,線(xiàn)程依然會(huì)處于BLOCKED狀態(tài),也就是說(shuō),interrupt()并不能使一個(gè)在等待鎖的線(xiàn)程真正”中斷”。我們看段代碼:

public class InterruptWaitingDemo extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) {try{//模擬任務(wù)代碼

Thread.sleep(2000);

}catch(InterruptedException e) {//... 清理操作//重設(shè)中斷標(biāo)志位

Thread.currentThread().interrupt();

}

}

System.out.println(isInterrupted());

}public static voidmain(String[] args) {

InterruptWaitingDemo thread= newInterruptWaitingDemo();

thread.start();try{

Thread.sleep(100);

}catch(InterruptedException e) {

}

thread.interrupt();

}

}

BLOCKED 如果線(xiàn)程在等待鎖,對(duì)線(xiàn)程對(duì)象調(diào)用 interrupt() 只是會(huì)設(shè)置線(xiàn)程的中斷標(biāo)志位,線(xiàn)程依然會(huì)處于 BLOCKED 狀態(tài),也就是說(shuō),interrupt() 并不能使一個(gè)在等待鎖的線(xiàn)程真正”中斷”。我們看段代碼:

public classInterruptSynchronizedDemo {private static Object lock = new Object();//monitor

private static class A extendsThread {

@Overridepublic voidrun() {//等待lock鎖

synchronized(lock) {//等待標(biāo)志位被置為true

while (!Thread.currentThread().isInterrupted()) {

}

}

System.out.println("exit");

}

}public static void test() throwsInterruptedException {synchronized (lock) {//獲取鎖

A a = newA();

a.start();

Thread.sleep(1000);//a在等待lock鎖,interrupt 無(wú)法中斷

a.interrupt();//a線(xiàn)程加入當(dāng)前線(xiàn)程,等待執(zhí)行完畢

a.join();

}

}public static void main(String[] args) throwsInterruptedException {

test();

}

}

test 方法在持有鎖 lock 的情況下啟動(dòng)線(xiàn)程 a,而線(xiàn)程 a 也去嘗試獲得鎖 lock,所以會(huì)進(jìn)入鎖等待隊(duì)列,隨后 test 調(diào)用線(xiàn)程 a 的 interrupt 方法并等待線(xiàn)程線(xiàn)程 a 結(jié)束,線(xiàn)程 a 會(huì)結(jié)束嗎?不會(huì),interrupt 方法只會(huì)設(shè)置線(xiàn)程的中斷標(biāo)志,而并不會(huì)使它從鎖等待隊(duì)列中出來(lái)。線(xiàn)程a 會(huì)一直嘗試獲取鎖,但是主線(xiàn)程也在等待 a 結(jié)束才會(huì)釋放鎖,所以相互之間互為等待,不能結(jié)束。

我們稍微修改下代碼,去掉 test方法中的最后一行 a.join(),即變?yōu)?#xff1a;

public static void test() throwsInterruptedException {synchronized(lock) {

A a= newA();

a.start();

Thread.sleep(1000);

a.interrupt();

}//lock鎖釋放后 A線(xiàn)程重隊(duì)列中出來(lái)

}

這時(shí),程序就會(huì)退出。為什么呢?因?yàn)橹骶€(xiàn)程不再等待線(xiàn)程 a 結(jié)束,釋放鎖 lock 后,線(xiàn)程 a 會(huì)獲得鎖,然后檢測(cè)到發(fā)生了中斷,所以會(huì)退出。

在使用 synchronized 關(guān)鍵字獲取鎖的過(guò)程中不響應(yīng)中斷請(qǐng)求,這是 synchronized 的局限性。如果這對(duì)程序是一個(gè)問(wèn)題,應(yīng)該使用顯式鎖,java 中的 Lock 接口,它支持以響應(yīng)中斷的方式獲取鎖。對(duì)于 Lock.lock(),可以改用 Lock.lockInterruptibly(),可被中斷的加鎖操作,它可以?huà)伋鲋袛喈惓!5韧诘却龝r(shí)間無(wú)限長(zhǎng)的 Lock.tryLock(long time, TimeUnit unit)。

NEW/TERMINATE

如果線(xiàn)程尚未啟動(dòng) (NEW),或者已經(jīng)結(jié)束 (TERMINATED),則調(diào)用 interrupt() 對(duì)它沒(méi)有任何效果,中斷標(biāo)志位也不會(huì)被設(shè)置。比如說(shuō),以下代碼的輸出都是 false。

public classInterruptNotAliveDemo {private static class A extendsThread {

@Overridepublic voidrun() {

}

}public static void test() throwsInterruptedException {

A a= newA();

a.interrupt();

System.out.println(a.isInterrupted());

a.start();

Thread.sleep(100);

a.interrupt();

System.out.println(a.isInterrupted());

}public static void main(String[] args) throwsInterruptedException {

test();

}

}

IO操作

如果線(xiàn)程在等待 IO 操作,尤其是網(wǎng)絡(luò) IO,則會(huì)有一些特殊的處理,我們沒(méi)有介紹過(guò)網(wǎng)絡(luò),這里只是簡(jiǎn)單介紹下。

實(shí)現(xiàn)此 InterruptibleChannel 接口的通道是可中斷的:如果某個(gè)線(xiàn)程在可中斷通道上因調(diào)用某個(gè)阻塞的 I/O 操作(常見(jiàn)的操作一般有這些:serverSocketChannel. accept()、socketChannel.connect、socketChannel.open、socketChannel.read、socketChannel.write、fileChannel.read、fileChannel.write)而進(jìn)入阻塞狀態(tài),而另一個(gè)線(xiàn)程又調(diào)用了該阻塞線(xiàn)程的 interrupt 方法,這將導(dǎo)致該通道被關(guān)閉,并且已阻塞線(xiàn)程接將會(huì)收到 ClosedByInterruptException,并且設(shè)置已阻塞線(xiàn)程的中斷狀態(tài)。另外,如果已設(shè)置某個(gè)線(xiàn)程的中斷狀態(tài)并且它在通道上調(diào)用某個(gè)阻塞的 I/O 操作,則該通道將關(guān)閉并且該線(xiàn)程立即接收到 ClosedByInterruptException;并仍然設(shè)置其中斷狀態(tài)。

如果線(xiàn)程阻塞于 Selector 調(diào)用,則線(xiàn)程的中斷標(biāo)志位會(huì)被設(shè)置,同時(shí),阻塞的調(diào)用會(huì)立即返回。

我們重點(diǎn)介紹另一種情況,InputStream 的 read 調(diào)用,該操作是不可中斷的,如果流中沒(méi)有數(shù)據(jù),read 會(huì)阻塞 (但線(xiàn)程狀態(tài)依然是 RUNNABLE ),且不響應(yīng) interrupt(),與 synchronized 類(lèi)似,調(diào)用 interrupt() 只會(huì)設(shè)置線(xiàn)程的中斷標(biāo)志,而不會(huì)真正”中斷”它,我們看段代碼

public classInterruptReadDemo {private static class A extendsThread {

@Overridepublic voidrun() {while(!Thread.currentThread().isInterrupted()){try{

System.out.println(System.in.read())//wait input

} catch(IOException e) {

e.printStackTrace();

}

}

System.out.println("exit");

}

}public static void main(String[] args) throwsInterruptedException {

A t= newA();

t.start();

Thread.sleep(100);

t.interrupt();

}

}

線(xiàn)程t啟動(dòng)后調(diào)用 System.in.read() 從標(biāo)準(zhǔn)輸入讀入一個(gè)字符,不要輸入任何字符,我們會(huì)看到,調(diào)用 interrupt() 不會(huì)中斷 read(),線(xiàn)程會(huì)一直運(yùn)行。

不過(guò),有一個(gè)辦法可以中斷 read() 調(diào)用,那就是調(diào)用流的 close 方法,我們將代碼改為:

public classInterruptReadDemo {private static class A extendsThread {

@Overridepublic voidrun() {while (!Thread.currentThread().isInterrupted()) {try{

System.out.println(System.in.read());

}catch(IOException e) {

e.printStackTrace();

}

}

System.out.println("exit");

}public voidcancel() {try{

System.in.close();

}catch(IOException e) {

}

interrupt();

}

}public static void main(String[] args) throwsInterruptedException {

A t= newA();

t.start();

Thread.sleep(100);

t.cancel();

}

}

我們給線(xiàn)程定義了一個(gè) cancel 方法,在該方法中,調(diào)用了流的 close 方法,同時(shí)調(diào)用了 interrupt 方法,這次,程序會(huì)輸出:

-1exit

也就是說(shuō),調(diào)用close方法后,read方法會(huì)返回,返回值為-1,表示流結(jié)束。

如何正確地取消/關(guān)閉線(xiàn)程

1. 以上,我們可以看出,interrupt 方法不一定會(huì)真正”中斷”線(xiàn)程,它只是一種協(xié)作機(jī)制,如果 不明白線(xiàn)程在做什么,不應(yīng)該貿(mào)然的調(diào)用線(xiàn)程的 interrupt 方法,以為這樣就能取消線(xiàn)程。

2. 對(duì)于以線(xiàn)程提供服務(wù)的程序模塊而言,它應(yīng)該封裝取消/關(guān)閉操作,提供單獨(dú)的取消/關(guān)閉方法給調(diào)用者,類(lèi)似于 InterruptReadDemo 中演示的 cancel 方法,外部調(diào)用者應(yīng)該調(diào)用這些方法而不是直接調(diào)用 interrupt。

3. Java并發(fā)庫(kù)的一些代碼就提供了單獨(dú)的取消/關(guān)閉方法,比如說(shuō),Future接口提供了如下方法以取消任務(wù):boolean cancel(boolean mayInterruptIfRunning);

4. 再比如,ExecutorService提供了如下兩個(gè)關(guān)閉方法:

voidshutdown();

List shutdownNow();

5. Future 和 ExecutorService 的 API 文檔對(duì)這些方法都進(jìn)行了詳細(xì)說(shuō)明,這是我們應(yīng)該學(xué)習(xí)的方式。

參考文章:

總結(jié)

以上是生活随笔為你收集整理的java 线程不足_Java 线程基础知识的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。