1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        面試官問:如何中斷一個線程,具體如何實現(xiàn)?

        共 3720字,需瀏覽 8分鐘

         ·

        2022-05-27 20:32

        中斷線程

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

        判斷線程是否被中斷

        判斷某個線程是否已被發(fā)送過中斷請求,請使用Thread.currentThread().isInterrupted()方法(因為它將線程中斷標示位設(shè)置為true后,不會立刻清除中斷標示位,即不會將中斷標設(shè)置為false),而不要使用thread.interrupted()(該方法調(diào)用后會將中斷標示位清除,即重新設(shè)置為false)方法來判斷,下面是線程在循環(huán)中時的中斷方式:

        while(!Thread.currentThread().isInterrupted()?&&?more?work?to?do){
        ????do?more?work
        }

        如何中斷線程

        如果一個線程處于了阻塞狀態(tài)(如線程調(diào)用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中斷的通道上的 I/O 操作方法后可進入阻塞狀態(tài)),則在線程在檢查中斷標示時如果發(fā)現(xiàn)中斷標示為true,則會在這些阻塞方法(sleep、join、wait、1.5中的condition.await及可中斷的通道上的 I/O 操作方法)調(diào)用處拋出InterruptedException異常,并且在拋出異常后立即將線程的中斷標示位清除,即重新設(shè)置為false。拋出異常是為了線程從阻塞狀態(tài)醒過來,并在結(jié)束線程前讓程序員有足夠的時間來處理中斷請求。

        注,synchronized在獲鎖的過程中是不能被中斷的,意思是說如果產(chǎn)生了死鎖,則不可能被中斷(請參考后面的測試例子)。與synchronized功能相似的reentrantLock.lock()方法也是一樣,它也不可中斷的,即如果發(fā)生死鎖,那么reentrantLock.lock()方法無法終止,如果調(diào)用時被阻塞,則它一直阻塞到它獲取到鎖為止。但是如果調(diào)用帶超時的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit),那么如果線程在等待時被中斷,將拋出一個InterruptedException異常,這是一個非常有用的特性,因為它允許程序打破死鎖。你也可以調(diào)用reentrantLock.lockInterruptibly()方法,它就相當于一個超時設(shè)為無限的tryLock方法。

        沒有任何語言方面的需求一個被中斷的線程應(yīng)該終止。中斷一個線程只是為了引起該線程的注意,被中斷線程可以決定如何應(yīng)對中斷。某些線程非常重要,以至于它們應(yīng)該不理會中斷,而是在處理完拋出的異常之后繼續(xù)執(zhí)行,但是更普遍的情況是,一個線程將把中斷看作一個終止請求,這種線程的run方法遵循如下形式:

        public?void?run()?{
        ????try?{
        ????????...
        ????????/*
        ?????????*?不管循環(huán)里是否調(diào)用過線程阻塞的方法如sleep、join、wait,這里還是需要加上
        ?????????*?!Thread.currentThread().isInterrupted()條件,雖然拋出異常后退出了循環(huán),顯
        ?????????*?得用阻塞的情況下是多余的,但如果調(diào)用了阻塞方法但沒有阻塞時,這樣會更安全、更及時。
        ?????????*/
        ????????while?(!Thread.currentThread().isInterrupted()&&?more?work?to?do)?{
        ????????????do?more?work
        ????????}
        ????}?catch?(InterruptedException?e)?{
        ????????//線程在wait或sleep期間被中斷了
        ????}?finally?{
        ????????//線程結(jié)束前做一些清理工作
        ????}
        }

        上面是while循環(huán)在try塊里,如果try在while循環(huán)里時,因該在catch塊里重新設(shè)置一下中斷標示,因為拋出InterruptedException異常后,中斷標示位會自動清除,此時應(yīng)該這樣:

        public?void?run()?{
        ????while?(!Thread.currentThread().isInterrupted()&&?more?work?to?do)?{
        ????????try?{
        ????????????...
        ????????????sleep(delay);
        ????????}?catch?(InterruptedException?e)?{
        ????????????Thread.currentThread().interrupt();//重新設(shè)置中斷標示
        ????????}
        ????}
        }

        底層中斷異常處理方式

        另外不要在你的底層代碼里捕獲InterruptedException異常后不處理,會處理不當,如下:

        void?mySubTask(){
        ????...
        ????try{
        ????????sleep(delay);
        ????}catch(InterruptedException?e){}//不要這樣做
        ????...
        }

        如果你不知道拋InterruptedException異常后如何處理,那么你有如下好的建議處理方式:

        1、在catch子句中,調(diào)用Thread.currentThread.interrupt()來設(shè)置中斷狀態(tài)(因為拋出異常后中斷標示會被清除),讓外界通過判斷Thread.currentThread().isInterrupted()標示來決定是否終止線程還是繼續(xù)下去,應(yīng)該這樣做:

        void?mySubTask()?{
        ????...
        ????try?{
        ????????sleep(delay);
        ????}?catch?(InterruptedException?e)?{
        ????????Thread.currentThread().isInterrupted();
        ????}
        ????...
        }

        2、或者,更好的做法就是,不使用try來捕獲這樣的異常,讓方法直接拋出:23 種設(shè)計模式實戰(zhàn)(很全)分享給你。

        void?mySubTask()?throws?InterruptedException?{
        ????...
        ????sleep(delay);
        ????...
        }

        中斷應(yīng)用

        使用中斷信號量中斷非阻塞狀態(tài)的線程

        中斷線程最好的,最受推薦的方式是,使用共享變量(shared variable)發(fā)出信號,告訴線程必須停止正在運行的任務(wù)。線程必須周期性的核查這一變量,然后有秩序地中止任務(wù)。Example2描述了這一方式:

        class?Example2?extends?Thread?{
        ????volatile?boolean?stop?=?false;//?線程中斷信號量

        ????public?static?void?main(String?args[])?throws?Exception?{
        ????????Example2?thread?=?new?Example2();
        ????????System.out.println("Starting?thread...");
        ????????thread.start();
        ????????Thread.sleep(3000);
        ????????System.out.println("Asking?thread?to?stop...");
        ????????//?設(shè)置中斷信號量
        ????????thread.stop?=?true;
        ????????Thread.sleep(3000);
        ????????System.out.println("Stopping?application...");
        ????}

        ????public?void?run()?{
        ????????//?每隔一秒檢測一下中斷信號量
        ????????while?(!stop)?{
        ????????????System.out.println("Thread?is?running...");
        ????????????long?time?=?System.currentTimeMillis();
        ????????????/*
        ?????????????*?使用while循環(huán)模擬?sleep?方法,這里不要使用sleep,否則在阻塞時會?拋
        ?????????????*?InterruptedException異常而退出循環(huán),這樣while檢測stop條件就不會執(zhí)行,
        ?????????????*?失去了意義。
        ?????????????*/
        ????????????while?((System.currentTimeMillis()?-?time?????????}
        ????????System.out.println("Thread?exiting?under?request...");
        ????}
        }

        使用thread.interrupt()中斷非阻塞狀態(tài)線程

        雖然Example2該方法要求一些編碼,但并不難實現(xiàn)。同時,它給予線程機會進行必要的清理工作。最新多線程面試題整理好了,點擊Java面試庫小程序在線刷題。

        這里需注意一點的是需將共享變量定義成volatile 類型或?qū)λ囊磺性L問封入同步的塊/方法(synchronized blocks/methods)中。上面是中斷一個非阻塞狀態(tài)的線程的常見做法,但對非檢測isInterrupted()條件會更簡潔:

        class?Example2?extends?Thread?{
        ????public?static?void?main(String?args[])?throws?Exception?{
        ????????Example2?thread?=?new?Example2();
        ????????System.out.println("Starting?thread...");
        ????????thread.start();
        ????????Thread.sleep(3000);
        ????????System.out.println("Asking?thread?to?stop...");
        ????????//?發(fā)出中斷請求
        ????????thread.interrupt();
        ????????Thread.sleep(3000);
        ????????System.out.println("Stopping?application...");
        ????}

        ????public?void?run()?{
        ????????//?每隔一秒檢測是否設(shè)置了中斷標示
        ????????while?(!Thread.currentThread().isInterrupted())?{
        ????????????System.out.println("Thread?is?running...");
        ????????????long?time?=?System.currentTimeMillis();
        ????????????//?使用while循環(huán)模擬?sleep
        ????????????while?((System.currentTimeMillis()?-?time?????????????}
        ????????}
        ????????System.out.println("Thread?exiting?under?request...");
        ????}
        }

        到目前為止一切順利!但是,當線程等待某些事件發(fā)生而被阻塞,又會發(fā)生什么?當然,如果線程被阻塞,它便不能核查共享變量,也就不能停止。這在許多情況下會發(fā)生,例如調(diào)用Object.wait()、ServerSocket.accept()DatagramSocket.receive()時,這里僅舉出一些。

        他們都可能永久的阻塞線程。即使發(fā)生超時,在超時期滿之前持續(xù)等待也是不可行和不適當?shù)?,所以,要使用某種機制使得線程更早地退出被阻塞的狀態(tài)。下面就來看一下中斷阻塞線程技術(shù)。

        使用thread.interrupt()中斷阻塞狀態(tài)線程

        Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,設(shè)置線程的中斷標示位,在線程受到阻塞的地方(如調(diào)用sleep、wait、join等地方)拋出一個異常InterruptedException,并且中斷狀態(tài)也將被清除,這樣線程就得以退出阻塞的狀態(tài)。下面是具體實現(xiàn):

        class?Example3?extends?Thread?{
        ????public?static?void?main(String?args[])?throws?Exception?{
        ????????Example3?thread?=?new?Example3();
        ????????System.out.println("Starting?thread...");
        ????????thread.start();
        ????????Thread.sleep(3000);
        ????????System.out.println("Asking?thread?to?stop...");
        ????????thread.interrupt();//?等中斷信號量設(shè)置后再調(diào)用
        ????????Thread.sleep(3000);
        ????????System.out.println("Stopping?application...");
        ????}

        ????public?void?run()?{
        ????????while?(!Thread.currentThread().isInterrupted())?{
        ????????????System.out.println("Thread?running...");
        ????????????try?{
        ????????????????/*
        ?????????????????*?如果線程阻塞,將不會去檢查中斷信號量stop變量,所?以thread.interrupt()
        ?????????????????*?會使阻塞線程從阻塞的地方拋出異常,讓阻塞線程從阻塞狀態(tài)逃離出來,并
        ?????????????????*?進行異常塊進行?相應(yīng)的處理
        ?????????????????*/
        ????????????????Thread.sleep(1000);//?線程阻塞,如果線程收到中斷操作信號將拋出異常
        ????????????}?catch?(InterruptedException?e)?{
        ????????????????System.out.println("Thread?interrupted...");
        ????????????????/*
        ?????????????????*?如果線程在調(diào)用?Object.wait()方法,或者該類的?join()?、sleep()方法
        ?????????????????*?過程中受阻,則其中斷狀態(tài)將被清除
        ?????????????????*/
        ????????????????System.out.println(this.isInterrupted());//?false

        ????????????????//中不中斷由自己決定,如果需要真真中斷線程,則需要重新設(shè)置中斷位,如果
        ????????????????//不需要,則不用調(diào)用
        ????????????????Thread.currentThread().interrupt();
        ????????????}
        ????????}
        ????????System.out.println("Thread?exiting?under?request...");
        ????}
        }

        一旦Example3中的Thread.interrupt()被調(diào)用,線程便收到一個異常,于是逃離了阻塞狀態(tài)并確定應(yīng)該停止。上面我們還可以使用共享信號量來替換!Thread.currentThread().isInterrupted()條件,但不如它簡潔。

        死鎖狀態(tài)線程無法被中斷

        Example4試著去中斷處于死鎖狀態(tài)的兩個線程,但這兩個線都沒有收到任何中斷信號(拋出異常),所以interrupt()方法是不能中斷死鎖線程的,因為鎖定的位置根本無法拋出異常:

        class?Example4?extends?Thread?{
        ????public?static?void?main(String?args[])?throws?Exception?{
        ????????final?Object?lock1?=?new?Object();
        ????????final?Object?lock2?=?new?Object();
        ????????Thread?thread1?=?new?Thread()?{
        ????????????public?void?run()?{
        ????????????????deathLock(lock1,?lock2);
        ????????????}
        ????????};
        ????????Thread?thread2?=?new?Thread()?{
        ????????????public?void?run()?{
        ????????????????//?注意,這里在交換了一下位置
        ????????????????deathLock(lock2,?lock1);
        ????????????}
        ????????};
        ????????System.out.println("Starting?thread...");
        ????????thread1.start();
        ????????thread2.start();
        ????????Thread.sleep(3000);
        ????????System.out.println("Interrupting?thread...");
        ????????thread1.interrupt();
        ????????thread2.interrupt();
        ????????Thread.sleep(3000);
        ????????System.out.println("Stopping?application...");
        ????}

        ????static?void?deathLock(Object?lock1,?Object?lock2)?{
        ????????try?{
        ????????????synchronized?(lock1)?{
        ????????????????Thread.sleep(10);//?不會在這里死掉
        ????????????????synchronized?(lock2)?{//?會鎖在這里,雖然阻塞了,但不會拋異常
        ????????????????????System.out.println(Thread.currentThread());
        ????????????????}
        ????????????}
        ????????}?catch?(InterruptedException?e)?{
        ????????????e.printStackTrace();
        ????????????System.exit(1);
        ????????}
        ????}
        }

        中斷I/O操作

        然而,如果線程在I/O操作進行時被阻塞,又會如何?I/O操作可以阻塞線程一段相當長的時間,特別是牽扯到網(wǎng)絡(luò)應(yīng)用時。例如,服務(wù)器可能需要等待一個請求(request),又或者,一個網(wǎng)絡(luò)應(yīng)用程序可能要等待遠端主機的響應(yīng)。23 種設(shè)計模式實戰(zhàn)(很全)分享給你。

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

        另外,如果已設(shè)置某個線程的中斷狀態(tài)并且它在通道上調(diào)用某個阻塞的 I/O 操作,則該通道將關(guān)閉并且該線程立即接收到 ClosedByInterruptException;并仍然設(shè)置其中斷狀態(tài)。如果情況是這樣,其代碼的邏輯和第三個例子中的是一樣的,只是異常不同而已。

        如果你正使用通道(channels)(這是在Java 1.4中引入的新的I/O API),那么被阻塞的線程將收到一個ClosedByInterruptException異常。但是,你可能正使用Java1.0之前就存在的傳統(tǒng)的I/O,而且要求更多的工作。既然這樣,Thread.interrupt()將不起作用,因為線程將不會退出被阻塞狀態(tài)。Example5描述了這一行為。盡管interrupt()被調(diào)用,線程也不會退出被阻塞狀態(tài),比如ServerSocket的accept方法根本不拋出異常。

        很幸運,Java平臺為這種情形提供了一項解決方案,即調(diào)用阻塞該線程的套接字的close()方法。在這種情形下,如果線程被I/O操作阻塞,當調(diào)用該套接字的close方法時,該線程在調(diào)用accept地方法將接收到一個SocketException(SocketException為IOException的子異常)異常,這與使用interrupt()方法引起一個InterruptedException異常被拋出非常相似,(注,如果是流因讀寫阻塞后,調(diào)用流的close方法也會被阻塞,根本不能調(diào)用,更不會拋IOExcepiton,此種情況下怎樣中斷?我想可以轉(zhuǎn)換為通道來操作流可以解決,比如文件通道)。

        下面是具體實現(xiàn):
        class?Example6?extends?Thread?{
        ????volatile?ServerSocket?socket;

        ????public?static?void?main(String?args[])?throws?Exception?{
        ????????Example6?thread?=?new?Example6();
        ????????System.out.println("Starting?thread...");
        ????????thread.start();
        ????????Thread.sleep(3000);
        ????????System.out.println("Asking?thread?to?stop...");
        ????????Thread.currentThread().interrupt();//?再調(diào)用interrupt方法
        ????????thread.socket.close();//?再調(diào)用close方法
        ????????try?{
        ????????????Thread.sleep(3000);
        ????????}?catch?(InterruptedException?e)?{
        ????????}
        ????????System.out.println("Stopping?application...");
        ????}

        ????public?void?run()?{
        ????????try?{
        ????????????socket?=?new?ServerSocket(8888);
        ????????}?catch?(IOException?e)?{
        ????????????System.out.println("Could?not?create?the?socket...");
        ????????????return;
        ????????}
        ????????while?(!Thread.currentThread().isInterrupted())?{
        ????????????System.out.println("Waiting?for?connection...");
        ????????????try?{
        ????????????????socket.accept();
        ????????????}?catch?(IOException?e)?{
        ????????????????System.out.println("accept()?failed?or?interrupted...");
        ????????????????Thread.currentThread().interrupt();//重新設(shè)置中斷標示位
        ????????????}
        ????????}
        ????????System.out.println("Thread?exiting?under?request...");
        ????}
        }

        一、沒有任何語言方面的需求一個被中斷的線程應(yīng)該終止。中斷一個線程只是為了引起該線程的注意,被中斷線程可以決定如何應(yīng)對中斷。

        最新多線程面試題整理好了,點擊Java面試庫小程序在線刷題。

        二、對于處于sleep,join等操作的線程,如果被調(diào)用interrupt()后,會拋出InterruptedException,然后線程的中斷標志位會由true重置為false,因為線程為了處理異常已經(jīng)重新處于就緒狀態(tài)。

        三、不可中斷的操作,包括進入synchronized段以及Lock.lock(),inputSteam.read()等,調(diào)用interrupt()對于這幾個問題無效,因為它們都不拋出中斷異常。如果拿不到資源,它們會無限期阻塞下去。

        對于Lock.lock(),可以改用Lock.lockInterruptibly(),可被中斷的加鎖操作,它可以拋出中斷異常。等同于等待時間無限長的Lock.tryLock(long time, TimeUnit unit)。

        對于inputStream等資源,有些(實現(xiàn)了interruptibleChannel接口)可以通過close()方法將資源關(guān)閉,對應(yīng)的阻塞也會被放開。

        首先,看看Thread類里的幾個方法:

        上面列出了與中斷有關(guān)的幾個方法及其行為,可以看到interrupt是中斷線程。如果不了解Java的中斷機制,這樣的一種解釋極容易造成誤解,認為調(diào)用了線程的interrupt方法就一定會中斷線程。

        其實,Java的中斷是一種協(xié)作機制。也就是說調(diào)用線程對象的interrupt方法并不一定就中斷了正在運行的線程,它只是要求線程自己在合適的時機中斷自己。每個線程都有一個boolean的中斷狀態(tài)(這個狀態(tài)不在Thread的屬性上),interrupt方法僅僅只是將該狀態(tài)置為true。

        比如對正常運行的線程調(diào)用interrupt()并不能終止他,只是改變了interrupt標示符。

        一般說來,如果一個方法聲明拋出InterruptedException,表示該方法是可中斷的,比如wait,sleep,join,也就是說可中斷方法會對interrupt調(diào)用做出響應(yīng)(例如sleep響應(yīng)interrupt的操作包括清除中斷狀態(tài),拋出InterruptedException),異常都是由可中斷方法自己拋出來的,并不是直接由interrupt方法直接引起的。

        Object.wait, Thread.sleep方法,會不斷的輪詢監(jiān)聽 interrupted 標志位,發(fā)現(xiàn)其設(shè)置為true后,會停止阻塞并拋出 InterruptedException異常。

        看了以上的說明,對java中斷的使用肯定是會了,但我想知道的是阻塞了的線程是如何通過interuppt方法完成停止阻塞并拋出interruptedException的,這就要看Thread中native的interuppt0方法了。

        第一步學習Java的JNI調(diào)用Native方法。

        第二步下載openjdk的源代碼,找到目錄結(jié)構(gòu)里的openjdk-src\jdk\src\share\native\java\lang\Thread.c文件。

        #include?"jni.h"
        #include?"jvm.h"

        #include?"java_lang_Thread.h"

        #define?THD?"Ljava/lang/Thread;"
        #define?OBJ?"Ljava/lang/Object;"
        #define?STE?"Ljava/lang/StackTraceElement;"

        #define?ARRAY_LENGTH(a)?(sizeof(a)/sizeof(a[0]))

        static?JNINativeMethod?methods[]?=?{
        ????{"start0",???????????"()V",????????(void?*)&JVM_StartThread},
        ????{"stop0",????????????"("?OBJ?")V",?(void?*)&JVM_StopThread},
        ????{"isAlive",??????????"()Z",????????(void?*)&JVM_IsThreadAlive},
        ????{"suspend0",?????????"()V",????????(void?*)&JVM_SuspendThread},
        ????{"resume0",??????????"()V",????????(void?*)&JVM_ResumeThread},
        ????{"setPriority0",?????"(I)V",???????(void?*)&JVM_SetThreadPriority},
        ????{"yield",????????????"()V",????????(void?*)&JVM_Yield},
        ????{"sleep",????????????"(J)V",???????(void?*)&JVM_Sleep},
        ????{"currentThread",????"()"?THD,?????(void?*)&JVM_CurrentThread},
        ????{"countStackFrames",?"()I",????????(void?*)&JVM_CountStackFrames},
        ????{"interrupt0",???????"()V",????????(void?*)&JVM_Interrupt},
        ????{"isInterrupted",????"(Z)Z",???????(void?*)&JVM_IsInterrupted},
        ????{"holdsLock",????????"("?OBJ?")Z",?(void?*)&JVM_HoldsLock},
        ????{"getThreads",????????"()["?THD,???(void?*)&JVM_GetAllThreads},
        ????{"dumpThreads",??????"(["?THD?")[["?STE,?(void?*)&JVM_DumpThreads},
        };

        #undef?THD
        #undef?OBJ
        #undef?STE
        JNIEXPORT?void?JNICALL
        Java_java_lang_Thread_registerNatives(JNIEnv?*env,?jclass?cls)
        {
        ????(*env)->RegisterNatives(env,?cls,?methods,?ARRAY_LENGTH(methods));
        }

        作者:零_壹
        來源:blog.csdn.net/xinxiaoyong100440105/article/details/80931705


        END


        推薦閱讀

        一鍵生成Springboot & Vue項目!【畢設(shè)神器】

        Java可視化編程工具系列(一)

        Java可視化編程工具系列(二)


        順便給大家推薦一個GitHub項目,這個 GitHub 整理了上千本常用技術(shù)PDF,絕大部分核心的技術(shù)書籍都可以在這里找到,

        GitHub地址:https://github.com/javadevbooks/books

        電子書已經(jīng)更新好了,你們需要的可以自行下載了,記得點一個star,持續(xù)更新中..




        瀏覽 29
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            午夜操逼| 久久午夜无码鲁丝午夜精品 | 大型女浴室洗澡视频偷拍在线播放 | 68日本xxxxxxxxx24 | 国产成人免费观看 | 波多野结衣在线视频免费看 | 美女航空毛片在线播放免费 | 欧美性生活一区二区三区 | 看操逼的视频 | 粗大黑人巨茎大战中国成人 |