1. 程式人生 > >由命令列掛起感悟設計-java異常機制思考、你不知道的ctrl+c

由命令列掛起感悟設計-java異常機制思考、你不知道的ctrl+c

1 問題複述

開發中遇到一個問題,在敲一個命令列的時候,karaf控制檯給掛起了,日誌沒有異常,通過ssh遠端連線還是可以訪問控制檯,但是簡單的回溯了一下程式碼,仔細一看,好像沒有問題,後來仔細跟蹤了以下命令列呼叫邏輯,在某個io操作中,使用了一個異常捕獲,然後在期望結果未給出的時候捕獲所有異常並死迴圈等待,所有結果返回,把邏輯簡要歸納以下,這也是我後來自己的一個測試程式碼,如下所示:

        if (debugstr != null) {
            long i=1;
            while(true) {
                try
{ i++; sleep(100); out.println("sleep time index:" + i); } catch (Exception e) { out.println(e); } }

是否初看程式碼覺得邏輯很清晰呢,只是簡簡單單捕獲了一下所有異常,能出多少事呢,或者這麼簡單的程式碼有什麼有問題很好定位的,可是當我們在實際開發中,把異常捕獲裡面的模組換成十幾個方法,幾千行程式碼,或許問題就沒那麼好定位了,也不會簡簡單單覺得就是幾行程式碼的問題。程式碼中包含的錯誤可能,真的很難發現,假如日誌加的不全,甚至影響到整個互動系統。甚至你的ctrl+c、ctrl+d都搞不定,只有重啟或者殺死程序,然後選擇軟體重新開始,可是這樣真的好嗎?

2 回溯1:為什麼ctrl+c、ctrl+d搞不定?

2.1 ctrl+c、ctrl+d、ctrl+d是什麼?

Ctrl+c和ctrl+z都是中斷命令,但是他們的作用卻不一樣.

  • Ctrl+c是強制中斷程式的執行。傳送 SIGINT 訊號給前臺程序組中的所有程序,強制終止程式的執行;

    在java執行過程中,例如上面例子的命令列執行,鍵盤上敲入ctrl+c,實際上是給控制檯觸發了java.lang.InterruptedException的異常,但是不好意思被捕獲到了,然後繼續迴圈,所以使用者退不出去,只能選擇繼續執行。

  • Ctrl+d :一個特殊的二進位制值,表示 EOF,作用相當於在終端中輸入exit後回車;

    上述程式碼的程式,敲入ctrl+d,你的控制檯的其他執行緒大概能退出來了,但是這個你死迴圈的程式碼,並沒有退出,如果同樣的程式碼關係到多個io,這些執行緒相當於你都搞不定了,因為你無法控制回收。

  • Ctrl+z傳送 SIGTSTP 訊號給前臺程序組中的所有程序,常用於掛起一個程序,而並非結束程序,使用者可以使用使用fg/bg操作恢復執行前臺或後臺的程序。fg命令在前臺恢復執行被掛起的程序,此時可以使用ctrl-z再次掛起該程序,bg命令在後臺恢復執行被掛起的程序,而此時將無法使用ctrl-z再次掛起該程序;可以將一個正在前臺執行的命令放到後臺,並且處於暫停狀態,不可執行;


    為了進一步弄清楚程序是否退出,打開了jvisualvm jdk執行緒分析工具,進一步瞭解,發現上述這個程式碼,讓ctrl+c,ctrl+d同時失效,只是ctrl+z雖然能退出繼續操作控制檯,但是想要重新去掉這個已知死迴圈的執行緒,也只能作業系統級別的使用程序命令殺死程序重新開始,簡而言之,jdk級別這個執行緒我們已經無法掌握。

    ​ 正常介面

01-正常

​ ctrl+d後進程顯示

ctrl_d

​ ctrl+z後進程顯示

01-ctrl_z

3 回溯2:java 異常你真的會用嗎?

java異常捕獲機制,從接觸io後,初使用它的一刻,真的發現了相對於c語言獨特的魅力,捕獲一個異常,然後做出相應的處理,比起c語言裡面因為異常簡單的程序就崩掉確實提供了太多的方便, 系統的穩定性得到了大幅提升。但是也正是因為這個機制的存在,有些程式猿開始犯起了懶:

  • 使用異常去控制正常的流程:

如果你的功能是正常的流程,只是不想非空判斷去捕捉空指標異常,這時機不是一個好的選擇,這些事情if else就能搞定,使用異常捕獲機制太大才小用了,而且影響效率;

  • 不分異常,盲目使用捕捉所有異常:

這是初級程式猿經常犯的一個錯,因為他們不瞭解異常,不知道jdk通常有哪些異常,然後又為了保證穩定性,保證自己的程式碼不受到任何干擾,所有簡單的一個catch Excetpion ,捕獲所有異常,看似沒有問題,空指標也捕獲了,io錯誤也捕獲,看似最優,但是實際上也堵死了自己能選擇的退出的路,例如上面的問題。

4 回溯3:我們的設計?

我們的設計帶來哪些感悟呢,我覺得問題雖小,但是作為一個剛剛邁過初級的程式小丁,我覺得確實有很深的驚醒意味,我們能做的事情還有更多:

  1. 程式設計規範:大部分程式設計規範大概都強調過異常的捕獲,不要試圖捕獲所有,一定要在一開始就貫徹執行。

  2. 完善的異常機制設計:我們在設計一個專案的時候,其實一開始除了正常的功能分析和拆解,也更應該留一個點精力給異常,想清楚會存在哪些異常:

    • 哪些是需要程式if else判斷去避免的?
    • 哪些什麼型別是需要我們統一捕獲去做一些糾正處理的?
    • 哪些異常無法描述,我們自己構建異常類,丟擲相應異常,在一個統一模組位置去處理?
    • 如果涉及到上下層互動,你的異常是否需要統一的錯誤碼?讓你的呼叫者或者使用者能及時感知異常?

    有了這些思考,一個系統才能有更好的人機互動性和穩定性,這是我們在設計支出絕對要花時間去考慮。

    正常功能分析、擴充套件性、異常功能分析,這三點任何專案都要在設計的時候就想清楚,不給自己留隱患,系統的思維分析這些問題;

5. 學習之路?

我們平時認為簡單的東西,實際上是不簡單的,例如文字裡面提到的ctrl+c,我們真的懂他的內部原理嗎?懂他與jdk 執行空間的互動嗎?對於大部分看中當下未深入研究的人,可能未必?但是它卻真正影響了我們程式碼。簡單的東西實際上不簡單?能從簡單的事情裡面去學習,也能收貨許多隱藏其後的心血。

還有一個點就是,設計通常是一個從簡單到複雜,再從複雜到簡單的過程,其實最後大道至簡,一個好的產品呈現在我們面前的時候肯定是簡單易用的?但是簡單的背後是什麼?更值得我們學習!

大道至簡,簡單背後的不簡單,更值得我們去反思,那個從簡單到複雜再回歸簡單的過程,正是進步的源泉!