由命令行挂起感悟设计-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 运行空间的交互吗?对于大部分看中当下未深入研究的人,可能未必?但是它却真正影响了我们代码。简单的东西实际上不简单?能从简单的事情里面去学习,也能收货许多隐藏其后的心血。

还有一个点就是,设计通常是一个从简单到复杂,再从复杂到简单的过程,其实最后大道至简,一个好的产品呈现在我们面前的时候肯定是简单易用的?但是简单的背后是什么?更值得我们学习!

大道至简,简单背后的不简单,更值得我们去反思,那个从简单到复杂再回归简单的过程,正是进步的源泉!

猜你喜欢

转载自blog.csdn.net/xinquanv1/article/details/83338259