这个Bug的排查之路,真的太有趣了。

沙海 2021年3月23日12:48:10杂谈 Java评论28字数 7146阅读23分49秒阅读模式
摘要

速读摘要

速读摘要文章源自JAVA秀-https://www.javaxiu.com/6354.html

当他把代码发给我,我在idea里面一粘贴,然后把main方法运行起来后,神奇的事情出现了。也就是说返回的值只代表你调用的那一刻有几个活跃线程,也许当你调用完成后,有一个线程就立马嗝屁了。方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难。线程去进行分发到各个不同的模块处理命令,并且返回处理结果。需要注意的是,这个端口并不是固定的,每次启动这个端口都会变化。文章源自JAVA秀-https://www.javaxiu.com/6354.html

原文约 3800 | 图片 45 | 建议阅读 8 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。

小黑格子屋 文章源自JAVA秀-https://www.javaxiu.com/6354.html

以下文章来源于why技术,作者why技术文章源自JAVA秀-https://www.javaxiu.com/6354.html

文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

why技术文章源自JAVA秀-https://www.javaxiu.com/6354.html

一个主要写代码,经常写文章,偶尔拍视频的风骚程序猿。文章源自JAVA秀-https://www.javaxiu.com/6354.html

文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

本文转载自公众号why技术文章源自JAVA秀-https://www.javaxiu.com/6354.html

邪乎到家必有鬼

在《深入理解Java虚拟机》一书中有这样一段代码:文章源自JAVA秀-https://www.javaxiu.com/6354.html

public class VolatileTest {    public static volatile int race = 0;    public static void increase() {        race++;    }    private static final int THREADS_COUNT=20;    public static void main(String[] args) {        Thread[] threads = new Thread[THREADS_COUNT];        for(int i = 0; i < THREADS_COUNT; i++){           new Thread(new Runnable() {               @Override               public void run() {                   for (int i = 0; i < 10000; i++) {                       increase();                   }               }           }).start();        }        //等待所有累加线程都结束        while(Thread.activeCount()>1)            Thread.yield();        System.out.println(race);    }}

你看到这段代码的第一反应是什么?文章源自JAVA秀-https://www.javaxiu.com/6354.html

是不是关注点都在 volatile 关键字上。文章源自JAVA秀-https://www.javaxiu.com/6354.html

甚至马上就要开始脱口而出:volatile 只保证可见性,不保证原子性。而代码中的 race++ 不是原子性的操作,巴拉巴拉巴拉...文章源自JAVA秀-https://www.javaxiu.com/6354.html

反正我就是这样的:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

当他把代码发给我,我在 idea 里面一粘贴,然后把 main 方法运行起来后,神奇的事情出现了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个代码真的没有执行到输出语句,也没有任何报错。文章源自JAVA秀-https://www.javaxiu.com/6354.html

看起来就像是死循环了一样。文章源自JAVA秀-https://www.javaxiu.com/6354.html

不信的话,你也可以放到你的 idea 里面去执行一下。文章源自JAVA秀-https://www.javaxiu.com/6354.html

等等......文章源自JAVA秀-https://www.javaxiu.com/6354.html

死循环?文章源自JAVA秀-https://www.javaxiu.com/6354.html

代码里面不是就有一个死循环吗?文章源自JAVA秀-https://www.javaxiu.com/6354.html

//等待所有累加线程都结束while(Thread.activeCount()>1)    Thread.yield();

这段代码能有什么小心思呢?看起来人畜无害啊。文章源自JAVA秀-https://www.javaxiu.com/6354.html

但是程序员的直觉告诉我,这个地方就是有问题的。文章源自JAVA秀-https://www.javaxiu.com/6354.html

活跃线程一直是大于 1 的,所以导致 while 一直在死循环。文章源自JAVA秀-https://www.javaxiu.com/6354.html

算了,不想了,先 Debug 看一眼吧。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Debug 了两遍之后,我才发现,这个事情,有点意思了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

因为 Debug 的情况下,程序竟然正常结束了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

啥情况啊?文章源自JAVA秀-https://www.javaxiu.com/6354.html

分析一波走起。文章源自JAVA秀-https://www.javaxiu.com/6354.html

为啥停不下来?

我是怎么分析这个问题的呢。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我就把程序又 Run 了起来,控制台还是啥输出都没有。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我就盯着这个控制台想啊,会是啥原因呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

这样干看着也不是办法啊。文章源自JAVA秀-https://www.javaxiu.com/6354.html

反正我现在就是咬死这个 while 循环是有问题的,所以为了排除其他的干扰项。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我把程序简化到了这个样子:文章源自JAVA秀-https://www.javaxiu.com/6354.html

public class VolatileTest {    public static volatile int race = 0;    public static void main(String[] args) {        while(Thread.activeCount()>1)            Thread.yield();        System.out.println("race = " + race);    }}

运行起来之后,还是没有执行到输出语句,也就侧面证实了我的想法:while 循环有问题。文章源自JAVA秀-https://www.javaxiu.com/6354.html

而 while 循环的条件就是 Thread.activeCount()>1文章源自JAVA秀-https://www.javaxiu.com/6354.html

朝着这个方向继续想下去,就是看看当前活跃线程到底有几个。文章源自JAVA秀-https://www.javaxiu.com/6354.html

于是程序又可以简化成这样:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

直接运行看到输出结果是 2。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

用 Debug 模式运行时返回的是 1。文章源自JAVA秀-https://www.javaxiu.com/6354.html

对比这运行结果,我心里基本上就有数了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

先看一下这个 activeCount 方法是干啥的:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

注意看画着下划线的地方:文章源自JAVA秀-https://www.javaxiu.com/6354.html

返回的值是一个 estimate。文章源自JAVA秀-https://www.javaxiu.com/6354.html

estimate 是啥?文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

你看,又在我这里学一个高级词汇。真是 very good。文章源自JAVA秀-https://www.javaxiu.com/6354.html

返回的是一个预估值。文章源自JAVA秀-https://www.javaxiu.com/6354.html

为什么呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

因为我们调用这个方法的一刻获取到值之后,线程数还是在动态变化的。文章源自JAVA秀-https://www.javaxiu.com/6354.html

也就是说返回的值只代表你调用的那一刻有几个活跃线程,也许当你调用完成后,有一个线程就立马嗝屁了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

所以,这个值是个预估值。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这一瞬间,我突然想到了量子力学中的测不准原理。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

你不可能同时知道一个粒子的位置和它的速度,就像在多线程高并发的情况下你不可能同时知道调用 activeCount 方法得到的值和你要用这个值的时刻,这个值的真实值是多少。文章源自JAVA秀-https://www.javaxiu.com/6354.html

你看,刚学完英语又学量子力学。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

好了,回到程序里面。文章源自JAVA秀-https://www.javaxiu.com/6354.html

虽然注释里面说了返回值是 estimate 的,但是在我们的程序中,并不存在这样的问题。文章源自JAVA秀-https://www.javaxiu.com/6354.html

看到 activeCount 方法的实现之后:文章源自JAVA秀-https://www.javaxiu.com/6354.html

public static int activeCount() {    return currentThread().getThreadGroup().activeCount();}

我又想到,既然在直接 Run 的情况下,程序返回的数是 2,那我看看到底有哪些线程呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

其实最开始我想着去 Debug 一下的,但是 Debug 的情况下,返回的数是 1。我意识到,这个问题肯定和 idea 有关,而且必须得用日志调试大法才能知道原因。文章源自JAVA秀-https://www.javaxiu.com/6354.html

于是,我把程序改成了这样:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

直接 Run 起来,可以看到,确实有两个线程。文章源自JAVA秀-https://www.javaxiu.com/6354.html

一个是 main 线程,我们熟悉。文章源自JAVA秀-https://www.javaxiu.com/6354.html

一个是 Monitor Ctrl-Break 线程,我不认识。文章源自JAVA秀-https://www.javaxiu.com/6354.html

但是当我用 Debug 的方式运行的时候,有意思的事情就发生了:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Monitor Ctrl-Break 线程不见了!?文章源自JAVA秀-https://www.javaxiu.com/6354.html

于是,我问他:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

是啊,问题解决了,但是啥原因啊?文章源自JAVA秀-https://www.javaxiu.com/6354.html

为什么 Run 不可以运行,而 Debug 可以运行呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

当前线程有哪些?

我们先梳理一下当前线程有哪些吧。文章源自JAVA秀-https://www.javaxiu.com/6354.html

可以使用下面的代码获取当前所有的线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

public  static Thread[] findAllThread(){    ThreadGroup currentGroup =Thread.currentThread().getThreadGroup();    while (currentGroup.getParent()!=null){        // 返回此线程组的父线程组        currentGroup=currentGroup.getParent();    }    //此线程组中活动线程的估计数    int noThreads = currentGroup.activeCount();    Thread[] lstThreads = new Thread[noThreads];    //把对此线程组中的所有活动子组的引用复制到指定数组中。    currentGroup.enumerate(lstThreads);    for (Thread thread : lstThreads) {        System.out.println("线程数量:"+noThreads+" " +                "线程id:" + thread.getId() +                 " 线程名称:" + thread.getName() +                 " 线程状态:" + thread.getState());    }    return lstThreads;}

运行之后可以看到有 6 个线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

也就是说,在 idea 里面,一个 main 方法 Run 起来之后,即使什么都不干,也会有 6 个线程运行。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这 6 个线程分别是干啥的呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

我们一个个的说。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Reference Handler 线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

JVM 在创建 main 线程后就创建 Reference Handler 线程,其优先级最高,为 10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Finalizer 线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个线程也是在 main 线程之后创建的,其优先级为10,主要用于在垃圾收集前,调用对象的 finalize() 方法。关于 Finalizer 线程的几点:1)只有当开始一轮垃圾收集时,才会开始调用 finalize() 方法;因此并不是所有对象的 finalize() 方法都会被执行;2)该线程也是 daemon 线程,因此如果虚拟机中没有其他非 daemon 线程,不管该线程有没有执行完 finalize() 方法,JVM 也会退出;3) JVM在垃圾收集时会将失去引用的对象包装成 Finalizer 对象(Reference的实现),并放入 ReferenceQueue,由 Finalizer 线程来处理;最后将该 Finalizer 对象的引用置为 null,由垃圾收集器来回收;4) JVM 为什么要单独用一个线程来执行 finalize() 方法呢?如果 JVM 的垃圾收集线程自己来做,很有可能由于在 finalize() 方法中误操作导致 GC 线程停止或不可控,这对 GC 线程来说是一种灾难。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Attach Listener 线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

Attach Listener 线程是负责接收到外部的命令,而对该命令进行执行的并且把结果返回给发送者。通常我们会用一些命令去要求 jvm 给我们一些反馈信息。如:java -version、jmap、jstack 等等。如果该线程在 jvm 启动的时候没有初始化,那么,则会在用户第一次执行 jvm 命令时,得到启动。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Signal Dispatcher 线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

前面我们提到第一个 Attach Listener 线程的职责是接收外部 jvm 命令,当命令接收成功后,会交给 signal dispather 线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather 线程也是在第一次接收外部 jvm 命令时,进行初始化工作。文章源自JAVA秀-https://www.javaxiu.com/6354.html

main 线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

呃,这个不说了吧。大家都知道。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Monitor Ctrl-Break 线程:文章源自JAVA秀-https://www.javaxiu.com/6354.html

先买个关子,下一小节专门聊聊这个线程。文章源自JAVA秀-https://www.javaxiu.com/6354.html

上面线程的作用,我是从这个网页搬运过来的,还有很多其他的线程,大家可以去看看:文章源自JAVA秀-https://www.javaxiu.com/6354.html

http://ifeve.com/jvm-thread/文章源自JAVA秀-https://www.javaxiu.com/6354.html

我好事做到底,直接给你来个长截图,一网打尽。文章源自JAVA秀-https://www.javaxiu.com/6354.html

你先把图片保存起来,后面慢慢看:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

现在跟着我去探寻 Monitor Ctrl-Break 线程的秘密。文章源自JAVA秀-https://www.javaxiu.com/6354.html

继续挖掘

问题解决了,但是问题背后的问题,还没有得到解决:文章源自JAVA秀-https://www.javaxiu.com/6354.html

Monitor Ctrl-Break 线程是啥?它是怎么来的?文章源自JAVA秀-https://www.javaxiu.com/6354.html

我们先 jstack 一把看看线程堆栈呗。文章源自JAVA秀-https://www.javaxiu.com/6354.html

而在 idea 里面,这里的“照相机”图标,就是 jstack 一样的功能。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我把程序恢复为最初的样子,然后把“照相机”就这么轻轻的一点:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

从线程堆栈里面可以看到 Monitor Ctrl-Break 线程来自于这个地方:文章源自JAVA秀-https://www.javaxiu.com/6354.html

com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)文章源自JAVA秀-https://www.javaxiu.com/6354.html

而这个地方,一看名称,是 idea 的源码了啊?文章源自JAVA秀-https://www.javaxiu.com/6354.html

不属于我们的项目里面了,这咋个搞呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

思考了一下,想到了一种可能,于是我决定用 jps 命令验证一下:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

看到执行结果的时候我笑了,一切就说的通了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

果然,是用了 -javaagent 啊。文章源自JAVA秀-https://www.javaxiu.com/6354.html

那么 javaagent 是什么?文章源自JAVA秀-https://www.javaxiu.com/6354.html

好的,要问答好这个问题,就得另起一篇文章了,本文不讨论,先欠着。文章源自JAVA秀-https://www.javaxiu.com/6354.html

只是简单的提一下。文章源自JAVA秀-https://www.javaxiu.com/6354.html

你在命令行执行 java 命令,会输出一大串东西,其中就包含这个:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

什么语言代理的,看不懂。文章源自JAVA秀-https://www.javaxiu.com/6354.html

叫我们参阅 java.lang.instrument。文章源自JAVA秀-https://www.javaxiu.com/6354.html

那它又是拿来干啥的?文章源自JAVA秀-https://www.javaxiu.com/6354.html

简单的一句话解释就是:文章源自JAVA秀-https://www.javaxiu.com/6354.html

使用 instrument 可以更加方便的使用字节码增强的技术,可以认为是一种 jvm 层面的截面。不需要对程序源代码进行任何侵入,就可以对其进行增强或者修改。总之,有点 AOP 内味。文章源自JAVA秀-https://www.javaxiu.com/6354.html

-javaagent 命令后面需要紧跟一个 jar 包。文章源自JAVA秀-https://www.javaxiu.com/6354.html

-javaagent:<jar 路径>[=<选项>]文章源自JAVA秀-https://www.javaxiu.com/6354.html

instrument 机制要求,这个 jar 包必须有 MANIFEST.MF 文件,而 MANIFEST.MF 文件里面必须有 Premain-Class 这个东西。文章源自JAVA秀-https://www.javaxiu.com/6354.html

所以,回到我们的程序中,看一下 javaagent 后面跟的包是什么。文章源自JAVA秀-https://www.javaxiu.com/6354.html

在哪看呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

就这个地方:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

你把它点开,命令非常的长。但是我们关心的 -javaagent 就在最开始的地方:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=61960文章源自JAVA秀-https://www.javaxiu.com/6354.html

可以看到,后面跟着的 jar 包是 idea_rt,按照文件目录找过去,也就是在这里:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我们解压这个 jar 包,打开它的 MANIFEST.MF 文件:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

而这个类,不就是我们要找的它吗:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

此时此刻,我们距离真相,只有一步之遥了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

进到对应的包里,发现有三个 class 类:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

主要关注 AppMainV2.class 文件:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

在这个文件里面,就有一个 startMonitor 方法:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我说过什么来着?文章源自JAVA秀-https://www.javaxiu.com/6354.html

来,大声的跟我念一遍:源码之下无秘密。文章源自JAVA秀-https://www.javaxiu.com/6354.html

Monitor Ctrl-Break 线程就是这里来的。文章源自JAVA秀-https://www.javaxiu.com/6354.html

而仔细看一眼这里的代码,这个线程在干啥事呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

Socket client = new Socket("127.0.0.1", portNumber);文章源自JAVA秀-https://www.javaxiu.com/6354.html

啊,我的天呐,来看看这个可爱的小东西,socket 编程,太熟悉了,简直是梦回大学实验课的时候。文章源自JAVA秀-https://www.javaxiu.com/6354.html

它是链接到 127.0.0.1 的某个端口上,然后 while(true) 死循环等待接收命令。文章源自JAVA秀-https://www.javaxiu.com/6354.html

那么这个端口是哪个端口呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

就是这里的 62325:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

需要注意的是,这个端口并不是固定的,每次启动这个端口都会变化。文章源自JAVA秀-https://www.javaxiu.com/6354.html

玩玩它

既然它是 Socket 编程,那么我就玩玩它呗。文章源自JAVA秀-https://www.javaxiu.com/6354.html

先搞个程序:文章源自JAVA秀-https://www.javaxiu.com/6354.html

public class SocketTest{    public static void main(String[] args) throws IOException {        ServerSocket serverSocket = new ServerSocket(12345);        System.out.println("等待客户端连接.");        Socket socket = serverSocket.accept();        System.out.println("有客户端连接上了 "+ socket.getInetAddress() + ":" + socket.getPort() +"");         OutputStream outputStream = socket.getOutputStream();        Scanner scanner = new Scanner(System.in);        while (true)        {            System.out.println("请输入指令: ");            String s = scanner.nextLine();            String message = s + "\n";            outputStream.write(message.getBytes("US-ASCII"));        }    }}

我们把服务端的端口指定为了 12345。文章源自JAVA秀-https://www.javaxiu.com/6354.html

客户端这边的端口也得指定为 12345,那怎么指定呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

别想复杂了,简单的一比。文章源自JAVA秀-https://www.javaxiu.com/6354.html

把这行日志粘贴出来:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

需要说明的是,我这边为了演示效果,在程序里面加了一个 for 循环。文章源自JAVA秀-https://www.javaxiu.com/6354.html

然后我们在这里把端口改为 12345:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

把文件保存为 start.bat 文件,随便放一个地方。文章源自JAVA秀-https://www.javaxiu.com/6354.html

万事俱备。文章源自JAVA秀-https://www.javaxiu.com/6354.html

我们先把服务端运行起来:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

然后,执行 bat 文件:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

在 cmd 窗口里面输出了我们的日志,说明程序正常运行。文章源自JAVA秀-https://www.javaxiu.com/6354.html

而在服务端这边,显示有客户端连接成功。文章源自JAVA秀-https://www.javaxiu.com/6354.html

叫我们输入指令。文章源自JAVA秀-https://www.javaxiu.com/6354.html

输入啥指令呢?文章源自JAVA秀-https://www.javaxiu.com/6354.html

看一下客户端支持哪些指令呗:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

可以看到,支持 STOP 命令。文章源自JAVA秀-https://www.javaxiu.com/6354.html

接受到该命令后,会退出程序。文章源自JAVA秀-https://www.javaxiu.com/6354.html

来,搞一波,动图走起:文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

搞定。文章源自JAVA秀-https://www.javaxiu.com/6354.html

好了,本文技术部分就到这里了,恭喜你知道了 idea 中的 Monitor Ctrl-Break 线程,这个学了没啥卵用的知识 。文章源自JAVA秀-https://www.javaxiu.com/6354.html

如果要深挖的话,往 -javaagent 方向挖一挖。文章源自JAVA秀-https://www.javaxiu.com/6354.html

应用很多的,比如耳熟能详的 Java 诊断工具 Arthas 就是基于 JavaAgent 做的。文章源自JAVA秀-https://www.javaxiu.com/6354.html

有点意思。文章源自JAVA秀-https://www.javaxiu.com/6354.html

-End-

文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这几个 Java 网站,有点刺激!文章源自JAVA秀-https://www.javaxiu.com/6354.html

文章源自JAVA秀-https://www.javaxiu.com/6354.html

文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

AOP 面试造火箭事件始末文章源自JAVA秀-https://www.javaxiu.com/6354.html

文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

让猛男害羞的AI特效,女友们纷纷笑成了开水壶;李逵、关羽:这是我???文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。 可乐记得加冰,爱我就要置顶 这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

这个Bug的排查之路,真的太有趣了。素质三连biubiubiu~这个Bug的排查之路,真的太有趣了。文章源自JAVA秀-https://www.javaxiu.com/6354.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定