首页 > 其他分享 >再使用System.out.println()打印收拾东西回家

再使用System.out.println()打印收拾东西回家

时间:2023-09-25 17:07:51浏览次数:38  
标签:输出 synchronized System println 日志 out


一、前言

今天没事干的时候,无意间点到了一个System.out.println()中的println()方法,一个闪亮的关键字冲击着我的眼睛。

不知道大家知不知道,那就是加锁的synchronized。但凡有锁的地方肯定会性能是有损耗的,当然得在并发的情况下!

synchronized在JDK8还是6之后进入了锁升级概念:无锁—>偏向锁—>自旋锁—>轻量级锁—>重量级锁

轻量级锁和偏向锁通常不涉及内核态切换

在到重量级锁时涉及到用户态和内核态的切换,很影响性能的!

有兴趣的可以自己研究一下synchronized的锁升级和降级!

下面我们回归正题,看一下这个System.out.println()

二、源码和分析

网上文章很多,大家想看System和out的详细解读,可以去看看哈,这里主要看一下println()这个方法的源码!

public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

我们以传进来的是Object对象为例,其他类型比这个少了一个转化为String在输出!

这种方式通常被称为对象级别的锁,因为它是以对象为粒度进行同步的!

print(s);newLine();这两个操作在同一时间只被一个线程执行,以防止输出的内容被多个线程交织在一起,导致输出混乱。

这种同步是为了保证输出的可见性和一致性。当然也带来了性能的损耗,一般情况不会引起线程阻塞,当多个线程尝试同时访问同一个控制台输出时,由于控制台输出的同步性质,可能会导致线程竞争锁,从而导致程序响应慢!

当然在没有并发的场景中,使用一下也没什么,切记不要在输出里面加上大量的运算和转换逻辑

但是为了不给后期埋雷,咱们还是尽量不要使用。

可以使用专门的日志框架,如Log4j、Logback等,它们提供了更灵活的日志记录和输出控制。

我们在查看一下:newLine()

我们会发现它也是一个加锁的方法!

private void newLine() {
    try {
        synchronized (this) {
            ensureOpen();
            textOut.newLine();
            textOut.flushBuffer();
            charOut.flushBuffer();
            if (autoFlush)
                out.flush();
        }
    }
    catch (InterruptedIOException x) {
        Thread.currentThread().interrupt();
    }
    catch (IOException x) {
        trouble = true;
    }
}

里面的textOut.flushBuffer()也是有锁的!

void flushBuffer() throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (nextChar == 0)
            return;
        out.write(cb, 0, nextChar);
        nextChar = 0;
    }
}

我们在看一下ensureOpen()方法,然后一直点就会发现 write(String chars, int start, int count)也是加着锁的!

所以我们还是不要轻易在项目中使用哈,咱们还是使用日志框架,还能有不用的日志级别打印,多香!

每天一个小技巧哈!

public void write(String str, int off, int len) throws IOException {
    synchronized (lock) {
        char cbuf[];
        if (len <= WRITE_BUFFER_SIZE) {
            if (writeBuffer == null) {
                writeBuffer = new char[WRITE_BUFFER_SIZE];
            }
            cbuf = writeBuffer;
        } else {    // Don't permanently allocate very large buffers.
            cbuf = new char[len];
        }
        str.getChars(off, (off + len), cbuf, 0);
        write(cbuf, 0, len);
    }
}

三、缺点和方案

1. 缺点

性能问题

使用 System.out.println() 输出信息到控制台通常比内存操作和计算操作慢得多。这是因为控制台输出涉及到文件I/O操作,需要将数据写入控制台。在高性能的应用程序中,频繁的输出会导致程序的性能下降,特别是在大规模数据处理和高并发情况下。

缺乏日志级别

System.out.println() 不支持不同的日志级别(如DEBUG、INFO、ERROR等),因此无法将输出信息分类为不同的重要性。在生产环境中,通常需要能够根据日志级别过滤和记录信息。

无法集中管理日志

System.out.println() 输出的信息通常散落在代码的各个地方,不容易集中管理。在实际应用中,通常需要将日志集中存储和管理,以便日后的监控、分析和故障排查。

2. 方案

我们上面也是说了,这里在强调一遍哈:

建议使用专门的日志框架来管理和控制日志输出,一些流行的 Java 日志框架包括:Log4j、Logback、SLF4J

四、总结

虽然 System.out.println() 在快速测试和学习阶段很有用,但在实际的应用程序中,频繁使用它可能会导致一系列问题。

因此,建议在生产环境中使用专门的日志框架来管理和控制日志输出,以便更好地满足应用程序的需求,并提高代码的可维护性和可扩展性。


看到这里了,还请动一下您的发财小手,关注一下公众号哈!!


标签:输出,synchronized,System,println,日志,out
From: https://blog.51cto.com/wangzhenjun/7597230

相关文章

  • python 缓存机制如何实现(cacheout)
    Python缓存机制可以使用第三方库cacheout来实现。cacheout提供了一个Cache类,它支持多种缓存策略,包括LRU、FIFO、LFU和TTL。Cache类的基本使用方法如下:1.安装cacheout:pipinstallcacheout2.导入Cache类:fromcacheoutimportCache3.创建Cache对象:cac......
  • Winform中使用System.Windows.Forms.Timer多次启动停止计时器时绑定事件会重复多次执
    场景C#中实现计时器功能(定时任务和计时多长时间后执行某方法):https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/106274074以上关于定时器的使用。在实现点击按钮启动定时器,点击停止按钮停止定时器时发现,重复多次后会导致定时器方法累计重复执行。联想到如下情况......
  • linux教程:route命令(显示并设置Linux中静态路由表)
    route显示并设置Linux中静态路由表补充说明route命令用来显示并设置Linux内核中的网络路由表,route命令设置的路由主要是静态路由。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。在Linux系统中设置路由通常是为了解决以下问题:该L......
  • 2023.9.24 ABout Math
    CF645F我们可以计算这样的函数\(F(x)\)表示\(\gcd\)是\(x\)的倍数有多少个\(k\)元组。设\(x\)的倍数有\(cnt_x\)个数,那么\(F(x)=C_{cnt_x}^k\)。根据莫反,\(f(x)=\sum_{x|d}F(d)\mu(d/x)\)\(Ans=\sumxf(x)=\sum_{x=1}^nx\sum_{x|d}\mu(d/x)\timesC_{cnt_d}......
  • 修复 GRUB unknown filesystem error
    出现问题的原因是我在安装好双系统后重新给硬盘进行了分区,GRUB的位置发生了变化Rescue部分参考:https://zhuanlan.zhihu.com/p/518428303但我没有办法按照上面的链接的方法进行修复和启动,于是按照这一篇下载了"boot-repair"自动修复,遂解决。......
  • C++ 的cout格式化输出
    在某些实际场景中,我们经常需要按照一定的格式输出数据,比如输出浮点数时保留2位小数,再比如以十六进制的形式输出整数,等等。对于学过C语言的读者应该知道,当使用printf()函数输出数据时,可以通过设定一些合理的格式控制符,来达到以指定格式输出数据的目的。例如%.2f表示输出浮点......
  • Kali使用zsteg出现"stack level too deep (SystemStackError)"报错!
    前段时间用VM虚拟机直接安装在kali官网下载的虚拟机镜像系统之后,安装完zsteg使用的时候出现"stackleveltoodeep(SystemStackError)"报错。在百度搜索许久也没有找到具体的解决方法,后来在Github里面发现也有人遇到了这个情况,并且提交了Issues。进Issues里面看了眼发现有人......
  • MySQL 出现 String Index out of range: x 的原因
    前言调试代码时遇到一个很奇怪的问题,对于一个很简单的sql,执行时会时不时的报异常StringIndexoutofrange:xSQL:select*fromtestwherearea=10但直接把SQL丢SQL工具里跑又没什么问题,百度了一圈都没找到有人有相同问题,明白后特地做下记录.原因由于要......
  • C++ 的cout.tellp()和cout.seekp()语法介绍
    无论是使用cout输出普通数据,用cout.put()输出指定字符,还是用cout.write()输出指定字符串,数据都会先放到输出流缓冲区,待缓冲区刷新,数据才会输出到指定位置(屏幕或者文件中)。值得一提的是,当数据暂存于输出流缓冲区中时,我们仍可以对其进行修改。ostream类中提供有tellp()和se......
  • 将Winform窗体程序缩到System tray的示例代码
    网上有很多将Winform窗体缩到Systemtray的示例,但多数不好用.这里是一个简单示例,使用了Visualstudio自带的NotifyIcon控件和一个快捷菜单contextMenuStrip控件.增加一个contextMenuStrip控件,新增两个菜单项:显示界面和退出系统.增加一个NotifyIcon控件,设置它......