System.out源码解读——err 和 out 一起用导致的顺序异常Bug

前言

笔者在写一个小 Demo 的过程中,发现了一个奇怪的问题。问题如下:

// 当 flag=true 时打印 a1 ;当 flag=false 时打印 a2。
public static void main(String[] args) {boolean flag = false;for (int i = 0; i < 10; i++) {if (flag) {System.out.println("a1");flag = false;} else {System.err.println("a2");flag = true;}}}

但当我们运行的时候,会发现控制台输出的顺序并不是我们所想的那样:

而真正我们理想状态下的顺序应该是:(此输出顺序需要我们打断点放慢程序执行速度)

笔者也是第一次注意到这个问题,所以也着实是摸不着头绪。不知道为什么会出现这种问题。那么就来阅读 System.out的源码来分析下这种问题。


一、out 和 err 的定义

JDK文档对两者的解释:

  • out:“标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。
  • err:“标准”错误输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。按照惯例,此输出流用于显示错误消息,或者显示那些即使用户输出流(变量 out 的值)已经重定向到通常不被连续监视的某一文件或其他目标,也应该立刻引起用户注意的其他信息。

二、源码解读

首先,System.out.println()在笔者看来要分成两部分:

  1. System.out;
  2. out.println();

我们点进去out发现,它其实就是System类的静态成员属性,类型为PrintStream。是一种系统自带的输出流。

    /*** “标准”输出流。此流已打开并准备接受输出数据。* 通常,此流对应于显示输出或主机环境或用户指定的另一个输出目的地。* 如果Console存在,则从字符到字节的转换中使用的编码等效于Console. charset(),* 否则等效于stdout.encoding。* See the {@code println} methods in class {@code PrintStream}.*/public static final PrintStream out = null;

那我们发现,out这个流被static final修饰,那么我们就可以直接通过类名.属性 (System.out)的方式来访问。同时,System这个类最上方有一块 静态代码块 用于初始化资源。会在程序被加载的时候最先调用。

 private static native void registerNatives();// 程序运行的时候最先加载。registerNatives() 方法是一个本地方法。
// 作用就是通过静态初始化器初始化资源。
static {registerNatives();
}

这就是初始化后的效果:


那我们再来看下println方法。println方法在PrintStream这个类中。 提供了多种重构形式:

笔者以我们最常用的println(String x)为例,带大家看看源码。

// 打印一个字符串,然后终止该行
public void println(String x) {// 判断是否是由 PrintStream 来调用。// 如果使用 System.out 的方法来调用,则永远为 True。if (getClass() == PrintStream.class) {writeln(String.valueOf(x));} else {// 同步代码块,保证同一时间只有一个线程进入。synchronized (this) {// 底层也是调用 implWriteln()print(x);newLine();}}
}// 如果判断为 True,则走此方法进行打印
private void writeln(char[] buf) {try {// 首先判断成员属性 lock 是否已被初始化。在 System.out 时就被静态代码块创建了if (lock != null) {lock.lock();  // 加锁,保证线程安全try {implWriteln(buf);} finally {lock.unlock();  // 解锁}} else {// 如果 lock 没有被初始化,则无法使用此锁。需要使用同步代码块来加锁synchronized (this) {implWriteln(buf);}}}catch (InterruptedIOException x) {Thread.currentThread().interrupt();}catch (IOException x) {trouble = true;}
}// 底层真正输出的实现方法
private void implWriteln(char[] buf) throws IOException {ensureOpen();  // 检查当前输出流,确保没有被关闭// textOut 和 charOut是:跟踪文本和字符输出流,以便在不刷新整个流的情况下刷新它们的缓冲区。textOut.write(buf);  // 输出字节数组textOut.newLine();  // 输出换行符号,帮助我们换行// 刷新流,保证不会有元素还在缓存区没输出// 底层其实就是判断当前流中的元素是否 = 0,如果 != 0 则调用 write() 方法在输出一次。textOut.flushBuffer();charOut.flushBuffer();if (autoFlush)out.flush();
}

问题原因及解决办法

当时阅读完 out 的源码后,我就在思考,会不会是两种不同的输出流导致线程冲突了。于是我使用 setErrerr 的输出流重定向为 out。再试一次结果:

public static void main(String[] args) {boolean flag = false;for (int i = 0; i < 10; i++) {if (flag) {System.out.println("a1");flag = false;} else {System.setErr(System.out);  // 就是这句话System.err.println("a2");flag = true;}}}

我们发现,确实输出顺序异常的问题解决了。那就证明我们的思路没问题。

但还是有问题:System.err打印不出红色。

这个原因我查了一下:System.err.println只能在屏幕上实现打印,即使你重定向了也一样。

总结

System.out.println()的性能并不好。当我们深入分析时,其调用顺序如下 println - > print - > write()+ newLine()。这个顺序流是Sun / Oracle JDK的实现。write()newLine()都包含一个synchronized块。同步有一点开销,但更多的是添加字符到缓冲区和打印的开销更大。

无论如何请勿使用System.out.println打印日志!

下篇文章将带大家探究为什么 System.out 和 System.err 一起运行会导致顺序异常的 Bug。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1534391.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于python的宠物信息交流系统---附源码74885

摘 要 在当今社会&#xff0c;随着人们生活质量的提高和对精神健康的追求&#xff0c;宠物已经成为我们生活中不可或缺的伙伴。与家中宠物朝夕相处&#xff0c;我们与宠物之间建立了深厚的情感纽带。然而&#xff0c;宠物也有可能生病&#xff0c;需要接受医疗护理。与人类一样…

如何利用Samba跨平台分享Ubuntu文件夹

1.安装Samba 终端输入sudo apt install samba 2.配置Samba 终端输入sudo vim /etc/samba/smb.conf 打开配置文件 滑动文件到最底下 输入以下内容 [Share] # 要共享的文件夹路径 path /home/xxx/sambashare read only no browsable yes编辑完成后按一下Esc按键后输入:wq回…

动不动就下跪的三星,离开天津了

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 三星辉煌岁月已过&#xff0c;万事凋零!如果说IBM、惠普、戴尔、苹果、富士康的离开有点惋惜的话&#xff0c;那三星的离开就是“活该”了。 成立于1993年的天津三星电子有限公司与2024年9月6日注销了&#xff0…

基于JavaWeb开发的java ssm springboot+VUE疫情防疫系统系统前后端分离设计和实现

基于JavaWeb开发的java ssm springbootVUE疫情防疫系统系统前后端分离设计和实现 &#x1f345; 作者主页 网顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取…

MySQL字符集的转换

背景介绍 在使用MySQL过程中&#xff0c;如果字符集配置不当&#xff0c;可能会出现插入失败、数据乱码、 索引失效、数据丢失、查询不到期望结果等一系列使用异常的情况。因此&#xff0c;熟练掌握MySQL字符集和比较规则的配置方法&#xff0c;并在此基础上了解MySQL字符集与…

AMD CMD UMD CommonJs ESM 的历史和区别

这几个东西都是用于定义模块规范的。有些资料会提及到这些概念&#xff0c;不理清楚非常容易困惑。 ESM&#xff08;ES Module&#xff09; 这个实际上我们是最熟悉的&#xff0c;就是ES6的模块功能。出的最晚&#xff0c;因为是官方出品&#xff0c;所以大势所趋&#xff0c…

股价跌破1美元!这家激光雷达上市公司被沃尔沃「拖进」ICU

作为目前前装上车成本最高的传感器之一&#xff0c;绝大部分激光雷达初创公司的表现&#xff0c;令人沮丧。“很多时候&#xff0c;前方似乎有一个美好的未来&#xff0c;但事情并不总是按照预期发展。” 比如&#xff0c;作为曾经美股市值最高&#xff08;曾经巅峰期高达120亿…

【kafka-02】kafka集群搭建

Kafka系列整体栏目 内容链接地址【一】afka安装和基本核心概念https://zhenghuisheng.blog.csdn.net/article/details/142213307【二】kafka集群搭建https://zhenghuisheng.blog.csdn.net/article/details/142253288 kafka集群搭建 一&#xff0c;kafka集群搭建1&#xff0c;ka…

C++——深部解析哈希

好久不见给大家分享一张图片吧 目录 前言 二、库文件 1、哈希冲突 2 哈希函数 3、闭散列 三 、闭散列的实现和底层逻辑 1、哈希表&#xff08;闭散列&#xff09;的定义 2、哈希表&#xff08;闭散列&#xff09;的插入 3、哈希表&#xff08;闭散列&#xff09;的查找 4.哈希表…

灵雀云DevOps:加速应用交付,点燃业务创新引擎

导语 近日&#xff0c;国际知名咨询机构Gartner发布了2024年度DevOps平台魔力象限报告&#xff08;Gartner Magic Quadrant for DevOps Platforms&#xff09;&#xff0c;为信息化决策者在技术战略层面提供了选型和评估DevOps平台供应商的全面视角。报告中&#xff0c;中国云…

MYSQL数据库——MYSQL管理

MYSQL数据库安装完成后&#xff0c;自带四个数据库&#xff0c;具体作用如下&#xff1a; 常用工具 1.mysql 不是指mysql服务&#xff0c;而是指mysql的客户端工具 例如&#xff1a; 2.mysqladmin 这是一个执行管理操作的客户端程序&#xff0c;可以用它来检查服务器的配置和…

DeviceNet网关HT3S-DNS-MDN读取七星华创CS310空气流量计数据应用案例

七星华创流量计CS310系列 (MODBUS RTU) 通过DeviceNet网关HT3S-DNS-MDN 与台达DVP系列的PLC进行交换数据应用案例 一、概述 本文主要介绍使用HI-TOP网关 HT3S-DNS-MDN在台达DVP系列 PLC和七星华创CS310流量计之间进行数据交换。 解决的问题&#xff1a;台达DVP系列如何通过…

pdf怎么压缩的小一点?4个方法帮你一键压缩

pdf怎么压缩的小一点&#xff1f;将PDF文件压缩得更小&#xff0c;不仅可以显著节省存储空间&#xff0c;还能加快文件在网络中的传输速度&#xff0c;让分享与备份变得更加便捷。特别是在处理大型文档或包含高分辨率图像的PDF时&#xff0c;压缩功能尤为重要。通过选择适合的压…

运维人员转行 AI 大模型全攻略:史上最详尽总结,一篇在手,转行无忧!

前言 做运维的苦&#xff0c;谁做谁懂。有时候真感觉自己就像个杂役&#xff0c;在公司都快成修电脑的了。不装了&#xff0c;我要转行&#xff01;在此给大家分享点经验&#xff0c;希望能帮到你们。 运维工程师若要转行至大模型领域&#xff0c;需要学习一系列全新的技能与…

解决RabbitMQ设置TTL过期后不进入死信队列

解决RabbitMQ设置TTL过期后不进入死信队列 问题发现问题解决方法一&#xff1a;只监听死信队列&#xff0c;在死信队列里面处理业务逻辑方法二&#xff1a;改为自动确认模式 问题发现 最近再学习RabbitMQ过程中&#xff0c;看到关于死信队列内容&#xff1a; 来自队列的消息可…

Google Dorks 发现隐藏的端点和参数

“作为一名漏洞赏金猎人&#xff0c;您的主要任务之一是绘制目标的攻击面&#xff0c;包括发现隐藏的参数和端点。这些可能会打开更深层次漏洞的大门&#xff0c;导致您可能未修补的 API 调用、未受保护的功能&#xff0c;甚至管理员级别的访问权限。Google Dorking 是一种非常…

AI智能体研发之路-模型篇(一):大模型训练框架LLaMA-Factory在国内网络环境下的安装、部署及使用

一、引言 贫富差距的产生是信息差&#xff0c;技术贫富差距的产生亦如此。如果可以自我发现或者在别人的指导下发现优秀的开源项目&#xff0c;学习或工作效率真的可以事半功倍。 今天力荐的项目是LLaMA-Factory&#xff0c;我在去年8月份就开始使用这个项目进行模型部署和微…

域控操作十七点五:域用户无管理员权限下安装IT打包的软件

1&#xff0c;需要软件Runasspcadmin三件套和winrar压缩软件 2&#xff0c;将需要打包的软件放进这个文件夹内&#xff0c;使用播放器举个例子 3&#xff0c;打开runasspcadmin.exe 按图片写就行了 文件夹现在是这样的然后全选右击&#xff0c;用WinRAR添加到压缩包 这个可以自…

第二百三十一节 JPA教程 - JPA Transient示例、 JPA ID注释示例

JPA教程 - JPA Transient示例 如果我们不想将属性保存到数据库&#xff0c;我们可以使用Transient注释标记该字段。 例子 以下代码来自Professor.java。 package cn.w3cschool.common; import java.util.Locale;import javax.persistence.Entity; import javax.persistence.…

面试官:说说你对vue的mixin的理解,有什么应用场景?

一、mixin是什么 Mixin是面向对象程序设计语言中的类&#xff0c;提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类 Mixin类通常作为功能模块使用&#xff0c;在需要该功能时“混入”&#xff0c;有利于代码复用又避免了多继承的复杂 Vue中的mixin 先来看一…