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()在笔者看来要分成两部分:
- System.out;
- 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 的源码后,我就在思考,会不会是两种不同的输出流导致线程冲突了。于是我使用 setErr 将 err 的输出流重定向为 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。
相关文章:
System.out源码解读——err 和 out 一起用导致的顺序异常Bug
前言 笔者在写一个小 Demo 的过程中,发现了一个奇怪的问题。问题如下: // 当 flagtrue 时打印 a1 ;当 flagfalse 时打印 a2。 public static void main(String[] args) {boolean flag false;for (int i 0; i < 10; i) {if (flag) {Sys…...
汽车软件开发之敏捷开发
一、前言 目前汽车电子产品,特别是汽车几大域控(如:智能座舱、智能驾驶、智能网联、车身控制)市场竞争激烈,消费者对汽车的需求逐渐多元化和个性化,用户对座舱和智驾产品的要求也越来越高。他们不仅要求产…...
ListBox显示最新数据、左移和右移操作
1、程序 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using static Sys…...
mysql实用系列:日期格式化
在MySQL中,你可以使用DATE_FORMAT()函数来格式化日期。DATE_FORMAT() 函数通常用于格式化 DATETIME 或 TIMESTAMP类型的字段。这个函数允许你按照指定的格式来显示日期和时间。下面是一些常见的日期格式化的例子: 显示年-月-日: SELECT DATE_…...
时钟频率、AI采样率与AO更新率的关系
在数据采集和信号生成设备(如NI板卡)中,时钟频率、AI(模拟输入)采样率、以及AO(模拟输出)更新率是三个至关重要的参数。它们共同决定了设备在信号采集与生成时的性能表现。本文将详细分析它们之…...
代理IP设置后IP不变?可能的原因及解决方法
在使用代理IP时,有时会遇到代理设置后IP地址却没有变化的情况。这种问题可能会让人感到困惑,但其实背后有多种原因。本文将详细探讨这些原因,并提供相应的解决方法,帮助你顺利解决问题。 可能的原因 代理IP设置后IP地址不变的原…...
瑞芯微RK3588开发板Linux系统添加自启动命令的方法,深圳触觉智能Arm嵌入式鸿蒙硬件方案商
本文适用于触觉智能所有Linux系统的开发板、主板添加自启动命令的方法,本次使用了触觉智能的EVB3588开发板演示,搭载了瑞芯微RK3588旗舰芯片。 该开发板为核心板加底板设计,为工业场景设计研发的模块化产品,10年以上稳定供货,帮助…...
Varjo在芬兰开设新工厂,以满足国防部门在XR模拟训练中的需求
在军事国防领域,全新技术的投入使用最看重的就是保密与安全。作为全球领先的XR头戴式显示器提供商Varjo,近日正式宣布将在位于芬兰的赫尔辛基开设一家新的安全制造工厂。 此次工厂扩建将使Varjo能够满足国防训练和模拟领域对其高分辨率XR解决方案日益增…...
python 识别省市、区县并组建三级信息数据库
一、网址: 全国行政区划信息查询平台 二、分析并搭建框架 检查网页源码: 检查网页源码可以发现: 所有省级信息全部在javaScript下的json中,会在页面加载时加载json数据,填充到页面的option中。 1、第一步:…...
家用小型超声波清洗机怎么选?四大人气爆款品牌不可错过!
在现代社会快节奏的步伐中,保持高度的个人及家居卫生标准成为了日常生活的重要组成部分,对于追求高端生活品质的人群而言,这一点尤为重要。因此,选取一款集高效与便利于一体的超声波清洗机,无疑是升级日常清洁体验的理…...
NVIDIA最新AI论文介绍NEST:一种用于语音处理的快速高效自监督模型
语音处理专注于开发能够分析、解释和生成人类语音的系统。这些技术涵盖了多种应用,例如自动语音识别(ASR)、说话人验证、语音转文本翻译以及说话人分离。随着对虚拟助手、转录服务和多语言交流工具的依赖不断增加,高效准确的语音处…...
聊聊对别人表示真正的关注
在工作和生活中,那些重要人士所得到的关注已经很多了,所以你不能只关注那些重要的人,对那些保洁门卫、前台等也需要我们给予真心的关注。 他们可使你的生活正常有序,但却经常被你忽略,见面打个招呼时常跟他们聊一聊,这…...
大数据-133 - ClickHouse 基础概述 全面了解
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
第1步win10宿主机与虚拟机通过NAT共享上网互通
VM的CentOS采用NAT共用宿主机网卡宿主机器无法连接到虚拟CentOS 要实现宿主机与虚拟机通信,原理就是给宿主机的网卡配置一个与虚拟机网关相同网段的IP地址,实现可以互通。 1、查看虚拟机的IP地址 2、编辑虚拟机的虚拟网络的NAT和DHCP的配置,…...
Python学习——【2.3】for循环
文章目录 【2.3】for循环一、for循环基础语法(一)基础语法※、练习 (二)range语句※、练习 (三)变量作用域 二、for循环嵌套使用※、练习 【2.3】for循环 一、for循环基础语法 (一)…...
Element UI:初步探索 Vue.js 的高效 UI 框架
Element UI:初步探索 Vue.js 的高效 UI 框架 一 . ElementUI 基本使用1.1 Element 介绍1.2 Element 快速入门1.3 基础布局1.4 容器布局1.5 表单组件1.6 表格组件1.6.1 基础表格1.6.2 带斑马纹表格1.6.3 带边框表格1.6.4 带状态的表格 1.7 导航栏组件讲解 二 . 学生列…...
React Native防止重复点击
项目中遇到了点击按钮重复提交的问题,防止重复点击首先是想到的是给点击事件一个定时,下次触发的条件是要距离上一次点击的时间大于N秒的之后才能再执行。 // 防重复点击函数 export const preventRepeatPress {lastPressTi1me: 0, // 上次点击时间…...
如何将Git本地代码推送到Gitee云端仓库
如何将Git本地代码推送到Gitee云端仓库 在使用Git进行版本控制时,将本地代码推送到远程仓库是一个基本且重要的操作。本文将详细介绍如何将你的Git本地代码推送到Gitee(码云)云端仓库。Gitee是一个国内非常流行的代码托管平台,类…...
架构师论文备考-论云原生架构及其应用
摘要 2022年3月,我有幸参与了公司的新智慧公交系统的研发工作。该系统基于B/S架构设计,并以多租户SaaS平台化为发展目标,旨在创建一个功能更全面、性能更卓越、稳定性更强、用户体验更佳的公交调度一体化平台。在这一项目中,我主要…...
12.java面向对象:java构造器
构造器 一个类即使什么都不写,也会存在一个方法。 假如我创建了一个类叫Student里面什么都不写,然后使用StudentDemo创建对象,运行没有结果,也没有报错。 public class Student {}public class StudentDemo {public static voi…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
