JAVA线程诡异问题!问了老手都不知道,求解答

清兮 2019-10-15 08:54:07


如图,为啥 打印异常的顺序错乱了? main是主线程, 应该一直按顺序执行下去才对??
...全文
163 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
旭烨 2019-10-18
  • 打赏
  • 举报
回复
谁告诉你主线程就一定执行完了,其他线程才能执行?你首先得了解什么是主线程,所谓主线程,首先它是一个非守护线程,然后,其实它和其他非守护线程是并列的,之所以加了一个主线程,只是因为主方法在这个线程。那么问题来了,主方法为什么是主呢?这个自己去百度吧,如果这个都不知道,日常劝退。
清兮 2019-10-18
  • 打赏
  • 举报
回复
引用 13 楼 qybao 的回复:
System.out调用println会自动调用flush的,所以你加不加flush效果一样。 https://docs.oracle.com/javase/8/docs/api/ flush的文档里有说明,并没有说flush会直接输出到设备,而是说把当前缓存信息输出到底层流。这种描述很暧昧,首先我们知道很多设备都有缓存,比如硬盘,cpu,显卡,网卡,声卡等等,都有缓存,那么好了,flush文档里所说的缓存其实是指java程序里的流管道缓存,底层流就是指系统设备的缓存。 你可以参考以下帖子的数据流的基本概念的输出流章节 https://juejin.im/entry/59291f1a44d9040064213c02 插图里的程序和设备之间有条管道,print的时候只是把信息往管道里送,管道满了才会往设备送,flush是立刻把管道的信息往设备送。这里往设备送并不代表信息马上输出,而是送到了设备的缓存,至于缓存的信息何时输出,那就不是jvm能管辖的了,是操作系统调度的。 所以主线程print,flush,只是把信息送到了设备的标准输出缓存,然后程序继续往下执行,抛出异常,jvm的print把异常信息送到了设备标准错误缓存,至于系统究竟会先从标准输出缓存取信息打印还是先从标准错误缓存取信息打印,就无法预知了。
学习了,没想到一个打印涉及这么多知识。谢谢
清兮 2019-10-17
  • 打赏
  • 举报
回复
引用 3 楼 API工具助手 的回复:
多线程日志打印跟程序执行不一定保持一致的,会出现程序已经执行但是日志还没打印的情况,打印日志也是一个线程
多谢回答. 我理解就是 其实整个程序的流程还是先执行遍历,再执行异常. 只是打印系统是不再是主线程, 而且system.println是一个线程A, 异常捕捉打印又是另外一个线程B, 所以A,B线程打印的东西出现错乱了.? (只是猜测)
qybao 2019-10-17
  • 打赏
  • 举报
回复
System.out调用println会自动调用flush的,所以你加不加flush效果一样。 https://docs.oracle.com/javase/8/docs/api/ flush的文档里有说明,并没有说flush会直接输出到设备,而是说把当前缓存信息输出到底层流。这种描述很暧昧,首先我们知道很多设备都有缓存,比如硬盘,cpu,显卡,网卡,声卡等等,都有缓存,那么好了,flush文档里所说的缓存其实是指java程序里的流管道缓存,底层流就是指系统设备的缓存。 你可以参考以下帖子的数据流的基本概念的输出流章节 https://juejin.im/entry/59291f1a44d9040064213c02 插图里的程序和设备之间有条管道,print的时候只是把信息往管道里送,管道满了才会往设备送,flush是立刻把管道的信息往设备送。这里往设备送并不代表信息马上输出,而是送到了设备的缓存,至于缓存的信息何时输出,那就不是jvm能管辖的了,是操作系统调度的。 所以主线程print,flush,只是把信息送到了设备的标准输出缓存,然后程序继续往下执行,抛出异常,jvm的print把异常信息送到了设备标准错误缓存,至于系统究竟会先从标准输出缓存取信息打印还是先从标准错误缓存取信息打印,就无法预知了。
wowpH 2019-10-17
  • 打赏
  • 举报
回复
怪不得报错的时候,红色的报错信息中间有时会出现一段输出。。。都是抢占资源的原因啊。
引用 4 楼 qybao 的回复:
首先,你要知道,系统标准输出设备属于共有系统资源 其次,你要知道,除了你的主线程,jvm内部还有其他的线程也在运行,比如垃圾回收线程等等 再次,你要知道,系统输出是有缓存的,你输出的信息系统会先保存到缓存中,缓存满了再输出 如果你想让输出缓存里的内容立刻输出到控制台,你可能调用System.out.flush() 明白了以上 那就知道,你的main线程和jvm线程(捕获异常打印)都要打印,都需要抢占系统标准输出设备(也就是获得系统资源锁),输出后信息只是保存在输出缓存,并没有马上输出到控制台,所以哪个线程抢到系统标准输出设备锁,不由你控制,所以输出缓存的信息也就不可控,不能按顺序保存,所以输出结果就会错乱
清兮 2019-10-17
  • 打赏
  • 举报
回复
引用 8 楼 qybao 的回复:
你没看明白或者是忽略输出缓存这部分内容 来具体分析打印操作的步骤 主线程:System.out.print->把信息写入标准输出缓存(如果缓存满了则阻塞,等待缓存内容输出到设备)->获取输出设备(也就是控制台)锁->缓存信息输出到设备 jvm异常打印线程:System.err.print->把信息写入标准错误缓存(如果缓存满了则阻塞,等待缓存内容输出到设备)->获取输出设备(也就是控制台)锁->缓存信息输出到设备 主线程确实是先执行person的打印,然后才抛出异常,jvm线程才开始打印异常信息的,也就是jvm打印发生在主线程打印之后,但是因为有输出缓存,信息并没有马上打印到控制台,在主线程的输出缓存信息没有完全打印到设备之前,jvm线程是有机会抢占到输出设备的,所以就可能乱序 而如果你主线程打印是加了等待0.01s,这个时间很有可能足够主线程的输出缓存信息打印到输出设备,而上面也说了,jvm打印发生在主线程之后,jvm打印的时候主线程的缓存信息已经输出完了,也就是jvm抢到抢不到输出设备锁都不影响结果了,所以也就不乱序了。
大哥啊,不是我抬杠. 你看我按你说的,加了flush方法,在打印下面,那我理解没走到异常之前,所有的循环打印都会被flush刷到控制台显示.. 但是实际结果不是这样,是我理解错了吗?
  • 打赏
  • 举报
回复
引用 7 楼 清兮 的回复:
[quote=引用 3 楼 API工具助手 的回复:]
多线程日志打印跟程序执行不一定保持一致的,会出现程序已经执行但是日志还没打印的情况,打印日志也是一个线程


多谢回答. 我理解就是 其实整个程序的流程还是先执行遍历,再执行异常.
只是打印系统是不再是主线程, 而且system.println是一个线程A, 异常捕捉打印又是另外一个线程B, 所以A,B线程打印的东西出现错乱了.?
(只是猜测)[/quote]感觉楼下这个解释就挺正确的
qybao 2019-10-17
  • 打赏
  • 举报
回复
补充一下 获取输出设备锁和缓存信息输出到设备是操作系统调度的,也就是跟主线程和jvm异常打印线程没直接关系,它们只负责到把信息写入输出缓存。如果想让缓存信息立刻输出到设备,之前也说了,可以调用flush方法
qybao 2019-10-17
  • 打赏
  • 举报
回复
你没看明白或者是忽略输出缓存这部分内容 来具体分析打印操作的步骤 主线程:System.out.print->把信息写入标准输出缓存(如果缓存满了则阻塞,等待缓存内容输出到设备)->获取输出设备(也就是控制台)锁->缓存信息输出到设备 jvm异常打印线程:System.err.print->把信息写入标准错误缓存(如果缓存满了则阻塞,等待缓存内容输出到设备)->获取输出设备(也就是控制台)锁->缓存信息输出到设备 主线程确实是先执行person的打印,然后才抛出异常,jvm线程才开始打印异常信息的,也就是jvm打印发生在主线程打印之后,但是因为有输出缓存,信息并没有马上打印到控制台,在主线程的输出缓存信息没有完全打印到设备之前,jvm线程是有机会抢占到输出设备的,所以就可能乱序 而如果你主线程打印是加了等待0.01s,这个时间很有可能足够主线程的输出缓存信息打印到输出设备,而上面也说了,jvm打印发生在主线程之后,jvm打印的时候主线程的缓存信息已经输出完了,也就是jvm抢到抢不到输出设备锁都不影响结果了,所以也就不乱序了。
清兮 2019-10-16
  • 打赏
  • 举报
回复
引用 4 楼 qybao 的回复:
首先,你要知道,系统标准输出设备属于共有系统资源 其次,你要知道,除了你的主线程,jvm内部还有其他的线程也在运行,比如垃圾回收线程等等 再次,你要知道,系统输出是有缓存的,你输出的信息系统会先保存到缓存中,缓存满了再输出 如果你想让输出缓存里的内容立刻输出到控制台,你可能调用System.out.flush() 明白了以上 那就知道,你的main线程和jvm线程(捕获异常打印)都要打印,都需要抢占系统标准输出设备(也就是获得系统资源锁),输出后信息只是保存在输出缓存,并没有马上输出到控制台,所以哪个线程抢到系统标准输出设备锁,不由你控制,所以输出缓存的信息也就不可控,不能按顺序保存,所以输出结果就会错乱
大哥,多谢指导.小弟还有点疑惑, 按照你的说法,那我设置person遍历打印拿每隔一S 打印一次, main线程打印的为什么就能一直抢到 输出设备锁呢? 如果是两个线程(main线程, jvm捕捉异常线程)一起走, JVM的线程还是有可能抢到锁才对啊.
sllhobo 2019-10-16
  • 打赏
  • 举报
回复
有没有在自学的小伙伴儿
qybao 2019-10-16
  • 打赏
  • 举报
回复
首先,你要知道,系统标准输出设备属于共有系统资源
其次,你要知道,除了你的主线程,jvm内部还有其他的线程也在运行,比如垃圾回收线程等等
再次,你要知道,系统输出是有缓存的,你输出的信息系统会先保存到缓存中,缓存满了再输出
如果你想让输出缓存里的内容立刻输出到控制台,你可能调用System.out.flush()

明白了以上
那就知道,你的main线程和jvm线程(捕获异常打印)都要打印,都需要抢占系统标准输出设备(也就是获得系统资源锁),输出后信息只是保存在输出缓存,并没有马上输出到控制台,所以哪个线程抢到系统标准输出设备锁,不由你控制,所以输出缓存的信息也就不可控,不能按顺序保存,所以输出结果就会错乱

  • 打赏
  • 举报
回复
多线程日志打印跟程序执行不一定保持一致的,会出现程序已经执行但是日志还没打印的情况,打印日志也是一个线程
kkkkk0lllll 2019-10-16
  • 打赏
  • 举报
回复
int a = 0/0
除0错误 ;除0错误 ;除0错误 ;
清兮 2019-10-15
  • 打赏
  • 举报
回复
加了0.1S的延迟 每次打印.. 反复试了多次,没有再出现那种情况. 如果真是 异常会在打在前面, 加了0.1S延迟,也会打印在前面的. 后面我又试了0.01S, 都没有 . 应该是IDEA显示的问题

50,503

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧