成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

詳細(xì)分析Java中斷機(jī)制

bergwhite / 2696人閱讀

摘要:沒有提供一種安全直接的方法來停止某個(gè)線程,但是提供了中斷機(jī)制。中斷的原理中斷機(jī)制是一種協(xié)作機(jī)制,也就是說通過中斷并不能直接終止另一個(gè)線程,而需要被中斷的線程自己處理中斷。線程的中斷狀態(tài)由該方法清除。測試線程是否已經(jīng)中斷。

1. 引言

當(dāng)我們點(diǎn)擊某個(gè)殺毒軟件的取消按鈕來停止查殺病毒時(shí),當(dāng)我們在控制臺(tái)敲入quit命令以結(jié)束某個(gè)后臺(tái)服務(wù)時(shí)……都需要通過一個(gè)線程去取消另一個(gè)線程正在執(zhí)行的任務(wù)。Java沒有提供一種安全直接的方法來停止某個(gè)線程,但是Java提供了中斷機(jī)制。

如果對Java中斷沒有一個(gè)全面的了解,可能會(huì)誤以為被中斷的線程將立馬退出運(yùn)行,但事實(shí)并非如此。中斷機(jī)制是如何工作的?捕獲或檢測到中斷后,是拋出InterruptedException還是重設(shè)中斷狀態(tài)以及在方法中吞掉中斷狀態(tài)會(huì)有什么后果?Thread.stop與中斷相比又有哪些異同?什么情況下需要使用中斷?本文將從以上幾個(gè)方面進(jìn)行描述。

2. 中斷的原理

Java中斷機(jī)制是一種協(xié)作機(jī)制,也就是說通過中斷并不能直接終止另一個(gè)線程,而需要被中斷的線程自己處理中斷。這好比是家里的父母叮囑在外的子女要注意身體,但子女是否注意身體,怎么注意身體則完全取決于自己。

Java中斷模型也是這么簡單,每個(gè)線程對象里都有一個(gè)boolean類型的標(biāo)識(shí)(不一定就要是Thread類的字段,實(shí)際上也的確不是,這幾個(gè)方法最終都是通過native方法來完成的),代表著是否有中斷請求(該請求可以來自所有線程,包括被中斷的線程本身)。例如,當(dāng)線程t1想中斷線程t2,只需要在線程t1中將線程t2對象的中斷標(biāo)識(shí)置為true,然后線程2可以選擇在合適的時(shí)候處理該中斷請求,甚至可以不理會(huì)該請求,就像這個(gè)線程沒有被中斷一樣。

java.lang.Thread類提供了幾個(gè)方法來操作這個(gè)中斷狀態(tài),這些方法包括:

public static boolean interrupted
測試當(dāng)前線程是否已經(jīng)中斷。線程的中斷狀態(tài) 由該方法清除。換句話說,如果連續(xù)兩次調(diào)用該方法,則第二次調(diào)用將返回 false(在第一次調(diào)用已清除了其中斷狀態(tài)之后,且第二次調(diào)用檢驗(yàn)完中斷狀態(tài)前,當(dāng)前線程再次中斷的情況除外)。

public boolean isInterrupted()
測試線程是否已經(jīng)中斷。線程的中斷狀態(tài)不受該方法的影響。

public void interrupt()
中斷線程。

其中,interrupt方法是唯一能將中斷狀態(tài)設(shè)置為true的方法。靜態(tài)方法interrupted會(huì)將當(dāng)前線程的中斷狀態(tài)清除,但這個(gè)方法的命名極不直觀,很容易造成誤解,需要特別注意。

上面的例子中,線程t1通過調(diào)用interrupt方法將線程t2的中斷狀態(tài)置為true,t2可以在合適的時(shí)候調(diào)用interrupted或isInterrupted來檢測狀態(tài)并做相應(yīng)的處理。

此外,類庫中的有些類的方法也可能會(huì)調(diào)用中斷,如FutureTask中的cancel方法,如果傳入的參數(shù)為true,它將會(huì)在正在運(yùn)行異步任務(wù)的線程上調(diào)用interrupt方法,如果正在執(zhí)行的異步任務(wù)中的代碼沒有對中斷做出響應(yīng),那么cancel方法中的參數(shù)將不會(huì)起到什么效果;又如ThreadPoolExecutor中的shutdownNow方法會(huì)遍歷線程池中的工作線程并調(diào)用線程的interrupt方法來中斷線程,所以如果工作線程中正在執(zhí)行的任務(wù)沒有對中斷做出響應(yīng),任務(wù)將一直執(zhí)行直到正常結(jié)束。

3. 中斷的處理

既然Java中斷機(jī)制只是設(shè)置被中斷線程的中斷狀態(tài),那么被中斷線程該做些什么?

處理時(shí)機(jī)

顯然,作為一種協(xié)作機(jī)制,不會(huì)強(qiáng)求被中斷線程一定要在某個(gè)點(diǎn)進(jìn)行處理。實(shí)際上,被中斷線程只需在合適的時(shí)候處理即可,如果沒有合適的時(shí)間點(diǎn),甚至可以不處理,這時(shí)候在任務(wù)處理層面,就跟沒有調(diào)用中斷方法一樣?!昂线m的時(shí)候”與線程正在處理的業(yè)務(wù)邏輯緊密相關(guān),例如,每次迭代的時(shí)候,進(jìn)入一個(gè)可能阻塞且無法中斷的方法之前等,但多半不會(huì)出現(xiàn)在某個(gè)臨界區(qū)更新另一個(gè)對象狀態(tài)的時(shí)候,因?yàn)檫@可能會(huì)導(dǎo)致對象處于不一致狀態(tài)。

處理時(shí)機(jī)決定著程序的效率與中斷響應(yīng)的靈敏性。頻繁的檢查中斷狀態(tài)可能會(huì)使程序執(zhí)行效率下降,相反,檢查的較少可能使中斷請求得不到及時(shí)響應(yīng)。如果發(fā)出中斷請求之后,被中斷的線程繼續(xù)執(zhí)行一段時(shí)間不會(huì)給系統(tǒng)帶來災(zāi)難,那么就可以將中斷處理放到方便檢查中斷,同時(shí)又能從一定程度上保證響應(yīng)靈敏度的地方。當(dāng)程序的性能指標(biāo)比較關(guān)鍵時(shí),可能需要建立一個(gè)測試模型來分析最佳的中斷檢測點(diǎn),以平衡性能和響應(yīng)靈敏性。

處理方式

1、 中斷狀態(tài)的管理

一般說來,當(dāng)可能阻塞的方法聲明中有拋出InterruptedException則暗示該方法是可中斷的,如BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep等,如果程序捕獲到這些可中斷的阻塞方法拋出的InterruptedException或檢測到中斷后,這些中斷信息該如何處理?一般有以下兩個(gè)通用原則:

如果遇到的是可中斷的阻塞方法拋出InterruptedException,可以繼續(xù)向方法調(diào)用棧的上層拋出該異常,如果是檢測到中斷,則可清除中斷狀態(tài)并拋出InterruptedException,使當(dāng)前方法也成為一個(gè)可中斷的方法。

若有時(shí)候不太方便在方法上拋出InterruptedException,比如要實(shí)現(xiàn)的某個(gè)接口中的方法簽名上沒有throws InterruptedException,這時(shí)就可以捕獲可中斷方法的InterruptedException并通過Thread.currentThread.interrupt()來重新設(shè)置中斷狀態(tài)。如果是檢測并清除了中斷狀態(tài),亦是如此。

一般的代碼中,尤其是作為一個(gè)基礎(chǔ)類庫時(shí),絕不應(yīng)當(dāng)吞掉中斷,即捕獲到InterruptedException后在catch里什么也不做,清除中斷狀態(tài)后又不重設(shè)中斷狀態(tài)也不拋出InterruptedException等。因?yàn)橥痰糁袛酄顟B(tài)會(huì)導(dǎo)致方法調(diào)用棧的上層得不到這些信息。

當(dāng)然,凡事總有例外的時(shí)候,當(dāng)你完全清楚自己的方法會(huì)被誰調(diào)用,而調(diào)用者也不會(huì)因?yàn)橹袛啾煌痰袅硕龅铰闊涂梢赃@么做。

總得來說,就是要讓方法調(diào)用棧的上層獲知中斷的發(fā)生。假設(shè)你寫了一個(gè)類庫,類庫里有個(gè)方法amethod,在amethod中檢測并清除了中斷狀態(tài),而沒有拋出InterruptedException,作為amethod的用戶來說,他并不知道里面的細(xì)節(jié),如果用戶在調(diào)用amethod后也要使用中斷來做些事情,那么在調(diào)用amethod之后他將永遠(yuǎn)也檢測不到中斷了,因?yàn)橹袛嘈畔⒁呀?jīng)被amethod清除掉了。如果作為用戶,遇到這樣有問題的類庫,又不能修改代碼,那該怎么處理?只好在自己的類里設(shè)置一個(gè)自己的中斷狀態(tài),在調(diào)用interrupt方法的時(shí)候,同時(shí)設(shè)置該狀態(tài),這實(shí)在是無路可走時(shí)才使用的方法。

2、 中斷的響應(yīng)

程序里發(fā)現(xiàn)中斷后該怎么響應(yīng)?這就得視實(shí)際情況而定了。有些程序可能一檢測到中斷就立馬將線程終止,有些可能是退出當(dāng)前執(zhí)行的任務(wù),繼續(xù)執(zhí)行下一個(gè)任務(wù)……作為一種協(xié)作機(jī)制,這要與中斷方協(xié)商好,當(dāng)調(diào)用interrupt會(huì)發(fā)生些什么都是事先知道的,如做一些事務(wù)回滾操作,一些清理工作,一些補(bǔ)償操作等。若不確定調(diào)用某個(gè)線程的interrupt后該線程會(huì)做出什么樣的響應(yīng),那就不應(yīng)當(dāng)中斷該線程。

4. Thread.interrupt VS Thread.stop

Thread.stop方法已經(jīng)不推薦使用了。而在某些方面Thread.stop與中斷機(jī)制有著相似之處。如當(dāng)線程在等待內(nèi)置鎖或IO時(shí),stop跟interrupt一樣,不會(huì)中止這些操作;當(dāng)catch住stop導(dǎo)致的異常時(shí),程序也可以繼續(xù)執(zhí)行,雖然stop本意是要停止線程,這么做會(huì)讓程序行為變得更加混亂。

那么它們的區(qū)別在哪里?最重要的就是中斷需要程序自己去檢測然后做相應(yīng)的處理,而Thread.stop會(huì)直接在代碼執(zhí)行過程中拋出ThreadDeath錯(cuò)誤,這是一個(gè)java.lang.Error的子類。

在繼續(xù)之前,先來看個(gè)小例子:

package com.ticmy.interrupt;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class TestStop {
    private static final int[] array = new int[80000];
    private static final Thread t = new Thread() {
        public void run() {
            try {
                System.out.println(sort(array));
            } catch (Error err) {
                err.printStackTrace();
            }
            System.out.println("in thread t");
        }
    };

    static {
        Random random = new Random();
        for(int i = 0; i < array.length; i++) {
            array[i] = random.nextInt(i + 1);
        }
    }

    private static int sort(int[] array) {
        for (int i = 0; i < array.length-1; i++){
            for(int j = 0 ;j < array.length - i - 1; j++){
                if(array[j] < array[j + 1]){
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
        return array[0];
    }

    public static void main(String[] args) throws Exception {
        t.start();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("go to stop thread t");
        t.stop();
        System.out.println("finish main");
    }
}

這個(gè)例子很簡單,線程t里面做了一個(gè)非常耗時(shí)的排序操作,排序方法中,只有簡單的加、減、賦值、比較等操作,一個(gè)可能的執(zhí)行結(jié)果如下:

go to stop thread t
java.lang.ThreadDeath
    at java.lang.Thread.stop(Thread.java:758)
    at com.ticmy.interrupt.TestStop.main(TestStop.java:44)
finish main
in thread t

這里sort方法是個(gè)非常耗時(shí)的操作,也就是說主線程休眠一秒鐘后調(diào)用stop的時(shí)候,線程t還在執(zhí)行sort方法。就是這樣一個(gè)簡單的方法,也會(huì)拋出錯(cuò)誤!換一句話說,調(diào)用stop后,大部分Java字節(jié)碼都有可能拋出錯(cuò)誤,哪怕是簡單的加法!

如果線程當(dāng)前正持有鎖,stop之后則會(huì)釋放該鎖。由于此錯(cuò)誤可能出現(xiàn)在很多地方,那么這就讓編程人員防不勝防,極易造成對象狀態(tài)的不一致。例如,對象obj中存放著一個(gè)范圍值:最小值low,最大值high,且low不得大于high,這種關(guān)系由鎖lock保護(hù),以避免并發(fā)時(shí)產(chǎn)生競態(tài)條件而導(dǎo)致該關(guān)系失效。假設(shè)當(dāng)前l(fā)ow值是5,high值是10,當(dāng)線程t獲取lock后,將low值更新為了15,此時(shí)被stop了,真是糟糕,如果沒有捕獲住stop導(dǎo)致的Error,low的值就為15,high還是10,這導(dǎo)致它們之間的小于關(guān)系得不到保證,也就是對象狀態(tài)被破壞了!如果在給low賦值的時(shí)候catch住stop導(dǎo)致的Error則可能使后面high變量的賦值繼續(xù),但是誰也不知道Error會(huì)在哪條語句拋出,如果對象狀態(tài)之間的關(guān)系更復(fù)雜呢?這種方式幾乎是無法維護(hù)的,太復(fù)雜了!如果是中斷操作,它決計(jì)不會(huì)在執(zhí)行l(wèi)ow賦值的時(shí)候拋出錯(cuò)誤,這樣程序?qū)τ趯ο鬆顟B(tài)一致性就是可控的。

正是因?yàn)榭赡軐?dǎo)致對象狀態(tài)不一致,stop才被禁用。

5. 中斷的使用

通常,中斷的使用場景有以下幾個(gè):

點(diǎn)擊某個(gè)桌面應(yīng)用中的取消按鈕時(shí);

某個(gè)操作超過了一定的執(zhí)行時(shí)間限制需要中止時(shí);

多個(gè)線程做相同的事情,只要一個(gè)線程成功其它線程都可以取消時(shí);

一組線程中的一個(gè)或多個(gè)出現(xiàn)錯(cuò)誤導(dǎo)致整組都無法繼續(xù)時(shí);

當(dāng)一個(gè)應(yīng)用或服務(wù)需要停止時(shí)。

下面來看一個(gè)具體的例子。這個(gè)例子里,本打算采用GUI形式,但考慮到GUI代碼會(huì)使程序復(fù)雜化,就使用控制臺(tái)來模擬下核心的邏輯。這里新建了一個(gè)磁盤文件掃描的任務(wù),掃描某個(gè)目錄下的所有文件并將文件路徑打印到控制臺(tái),掃描的過程可能會(huì)很長。若需要中止該任務(wù),只需在控制臺(tái)鍵入quit并回車即可。

package com.ticmy.interrupt;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

public class FileScanner {
    private static void listFile(File f) throws InterruptedException {
        if(f == null) {
            throw new IllegalArgumentException();
        }
        if(f.isFile()) {
            System.out.println(f);
            return;
        }
        File[] allFiles = f.listFiles();
        if(Thread.interrupted()) {
            throw new InterruptedException("文件掃描任務(wù)被中斷");
        }
        for(File file : allFiles) {
            //還可以將中斷檢測放到這里
            listFile(file);
        }
    }

    public static String readFromConsole() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            return reader.readLine();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) throws Exception {
        final Thread fileIteratorThread = new Thread() {
            public void run() {
                try {
                    listFile(new File("c:"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread() {
            public void run() {
                while(true) {
                    if("quit".equalsIgnoreCase(readFromConsole())) {
                        if(fileIteratorThread.isAlive()) {
                            fileIteratorThread.interrupt();
                            return;
                        }
                    } else {
                        System.out.println("輸入quit退出文件掃描");
                    }
                }
            }
        }.start();
        fileIteratorThread.start();
    }
}

在掃描文件的過程中,對于中斷的檢測這里采用的策略是,如果碰到的是文件就不檢測中斷,是目錄才檢測中斷,因?yàn)槲募赡苁欠浅6嗟?,每次遇到文件都檢測一次會(huì)降低程序執(zhí)行效率。此外,在fileIteratorThread線程中,僅是捕獲了InterruptedException,沒有重設(shè)中斷狀態(tài)也沒有繼續(xù)拋出異常,因?yàn)槲曳浅G宄氖褂铆h(huán)境,run方法的調(diào)用棧上層已經(jīng)沒有可能需要檢測中斷狀態(tài)的方法了。

在這個(gè)程序中,輸入quit完全可以執(zhí)行System.exit(0)操作來退出程序,但正如前面提到的,這是個(gè)GUI程序核心邏輯的模擬,在GUI中,執(zhí)行System.exit(0)會(huì)使得整個(gè)程序退出。

6. 參考資料

《Java Concurrency in Practice》

《Concurrent Programming in Java Design principles and patterns》

http://docs.oracle.com/javase/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html

via ifeve

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/64048.html

相關(guān)文章

  • java

    摘要:多線程編程這篇文章分析了多線程的優(yōu)缺點(diǎn),如何創(chuàng)建多線程,分享了線程安全和線程通信線程池等等一些知識(shí)。 中間件技術(shù)入門教程 中間件技術(shù)入門教程,本博客介紹了 ESB、MQ、JMS 的一些知識(shí)... SpringBoot 多數(shù)據(jù)源 SpringBoot 使用主從數(shù)據(jù)源 簡易的后臺(tái)管理權(quán)限設(shè)計(jì) 從零開始搭建自己權(quán)限管理框架 Docker 多步構(gòu)建更小的 Java 鏡像 Docker Jav...

    honhon 評論0 收藏0
  • Java Executors 源碼分析

    摘要:表示一個(gè)異步任務(wù)的結(jié)果,就是向線程池提交一個(gè)任務(wù)后,它會(huì)返回對應(yīng)的對象。它們分別提供兩個(gè)重要的功能阻塞當(dāng)前線程等待一段時(shí)間直到完成或者異常終止取消任務(wù)。此時(shí),線程從中返回,然后檢查當(dāng)前的狀態(tài)已經(jīng)被改變,隨后退出循環(huán)。 0 引言 前段時(shí)間需要把一個(gè)C++的項(xiàng)目port到Java中,因此時(shí)隔三年后重新熟悉了下Java。由于需要一個(gè)通用的線程池,自然而然就想到了Executors。 用了...

    itvincent 評論0 收藏0
  • Thread類源碼解讀(3)——線程中斷interrupt

    摘要:現(xiàn)在終止一個(gè)線程,基本上只能靠曲線救國式的中斷來實(shí)現(xiàn)。中斷機(jī)制的核心在于中斷狀態(tài)和異常中斷狀態(tài)設(shè)置一個(gè)中斷狀態(tài)清除一個(gè)中斷狀態(tài)方法同時(shí)會(huì)返回線程原來的中斷的狀態(tài)。中斷異常中斷異常一般是線程被中斷后,在一些類型的方法如中拋出。 前言 系列文章目錄 線程中斷是一個(gè)很重要的概念,通常,取消一個(gè)任務(wù)的執(zhí)行,最好的,同時(shí)也是最合理的方法,就是通過中斷。 本篇我們主要還是通過源碼分析來看看中斷的概...

    fevin 評論0 收藏0
  • 我的阿里之路+Java面經(jīng)考點(diǎn)

    摘要:我的是忙碌的一年,從年初備戰(zhàn)實(shí)習(xí)春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實(shí)習(xí)。因?yàn)槲倚睦砗芮宄?,我的目?biāo)是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學(xué)習(xí)計(jì)劃,將我的短期目標(biāo)更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實(shí)習(xí)春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實(shí)習(xí)offer。然后五月懷著忐忑的心情開始了螞蟻金...

    姘擱『 評論0 收藏0
  • 記一次PHP并發(fā)性能調(diào)優(yōu)實(shí)戰(zhàn) -- 性能提升104%

    摘要:這是多處理器系統(tǒng)中,調(diào)度器用來分散任務(wù)到不同的機(jī)制,通常也被稱為處理器間中斷,。文章編寫計(jì)劃 待完成: 詳細(xì)介紹用到的各個(gè)工具 作者: 萬千鈞(祝星) 適合閱讀人群 文中的調(diào)優(yōu)思路無論是php, java, 還是其他任何語言都是用. 如果你有php使用經(jīng)驗(yàn), 那肯定就更好了 業(yè)務(wù)背景 框架及相應(yīng)環(huán)境 laravel5.7, mysql5.7, redis5, nginx1.15 cento...

    番茄西紅柿 評論0 收藏0

發(fā)表評論

0條評論

bergwhite

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<