守护线程在logback中的使用

先说问题,在java应用中,logback的异步Appender是怎么在主线程结束后,停下来的?

复盘

我在一个logback的测试用例中,写了这样的代码和logback的配置:

// 一个测试类,main函数中简单的打印了几行日志
public class Test { private static final Logger LOGGER = LoggerFactory.getLogger(Test.class); public static void main(String[] args) {
LOGGER.info("test log 1");
LOGGER.info("test log 2");
} }

这是我使用的logback的配置,为root-logger配置了两个appender,一个是ConsoleAppender,会将日志内容输出到控制台中,另一个是套在AsyncAppender中的FileAppender,它采用异步的方式将日志内容输出到文件中

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/usr/local/test.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<root level="debug">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC"/>
</root>
</configuration>

当我正常执行Test.main(),观察控制台和文件中都正常输出了日志。然后,为了观察AsyncAppender的原理,我在AsyncAppender的父类AsyncAppenderBase中,看了一下大致的原理:AsyncAppenderBasestart()方法中,调用了worker.start(),其实就是启动了一个线程。截取Worker.run()方法中比较重要的代码,就是在一个while循环中,从阻塞队列获取ILoggerEvent,我在这里打了断点,希望追进具体的Appender中看一下

while (parent.isStarted()) {
try {
E e = parent.blockingQueue.take();
// 这里打了断点
aai.appendLoopOnAppenders(e);
} catch (InterruptedException ie) {
break;
}
}

当执行到断点处,观察了一会儿变量后,点击步进,突然发现整个java应用结束了。没有任何异常发生。这种情况我重试了很多次,不一定在断点处结束,可能再往下几步,但是debug模式下,java应用一定会在某个点毫无征兆的“正常结束”

我当时去确认了一下,控制台中两行日志都打印了出来,这说明主线程一定执行完了,但是我当时的知识储备是,java主线程结束时,如果子线程没有结束,那么会等待子线程结束后,JVM才会执行最后的结束任务。(这里完全不记得守护线程的概念)

!!!上面这个结论是错误的

然后就想通过jstack来观测一下,当main-Thread结束后,线程到底是什么情况:

这里发现:

DestroyJavaVM线程居然处于RUNNABLE阶段,可是还有子线程在执行啊,为什么它就开始了

带着疑问再看一下AsyncAppender-Worker-ASYNC线程,这就是异步日志打印任务的线程了,也是RUNNABLE状态,但是它好像是一个守护线程(daemon),这才想起来java线程的基础知识,用户线程守护线程,这里直接贴出来ThreadsetDaemon方法,代码注释中说:标记该线程为user thread或daemon thread,JVM只会在当前运行的线程,全部都是daemon thread时,才会结束。

再看看AsyncAppenderBase中的start()方法:

可以看到,它将worker指定为了daemon线程,问题到这里其实也就解决了。

结论

这次碰到的问题,其实并不复杂,主要还是知识储备的问题

java多线程中,用户线程和守护线程在能力上并没有什么区别,最重要的区别是当JVM退出时,如果还有存活的用户线程,则不会退出;如果只剩下守护线程了,那么JVM则会杀掉所有的守护线程,并退出。

PS:

没人知道我DEBUG了多久。。。简直超出认知范围了!!!差点以为是玄学

守护线程在logback中的使用 - 论基础知识的重要性的更多相关文章

  1. Java中实现异常处理的基础知识

    Java中实现异常处理的基础知识 异常 (Exception):发生于程序执行期间,表明出现了一个非法的运行状况.许多JDK中的方法在检测到非法情况时,都会抛出一个异常对象. 例如:数组越界和被0除. ...

  2. react组件在项目中的应用(基础知识)

    上图我是定义了5个模块,全部都渲染在一个组件里面.可以先看看我的代码结构 我将Hello文件夹下的index.jsx文件作为父组件,最后渲染在根组件中. 那我们怎么输出这个Hello组件呢?要达到上图 ...

  3. 【Unity】6.1 Unity中的C#脚本基础知识

    分类:Unity.C#.VS2015 创建日期:2016-04-16 一.简介 1.常用的C#数据类型 这里简单介绍用Unity开发游戏时,最常用的一些数据类型. (1)基本类型 int.float. ...

  4. C++面试中可能考察的基础知识(1)

    1 C++中允许函数的嵌套调用,但不允许函数的嵌套定义 2 构建派生类对象时,先调用基类的构造函数,在调用成员对象的构造函数,最后调用派生类构造函数. 3 volatile关键字 volatile提醒 ...

  5. 第11天:JS中变量、字符串基础知识

    一.js简介用来制作页面交互效果,提高用户体验. js页面效果:轮播图.选项卡.地图.表单验证javascript是弱变量类型的语言,变量只需要用var来声明.而java要根据变 量类型来声明, in ...

  6. java守护线程的理解

    package daemonThread; /*setDaemon(true)方法将线程设置为守护线程,线程的Daemon默认值为false * 只要当前JVM实例中存在任何一个非守护线程没有结束,守 ...

  7. 多线程系列 - 基础篇01 - 线程基本概念 & 线程优先级 & 守护线程 60%

    1.什么是线程 将线程理解为轻量级进程,它与进程的最大的区别是: 多个线程共享一个进程资源: 对于OS的许多资源的分配和管理(如内存)通常都是进程级别的,线程只是os调度的最小单位: 相对于进程来说更 ...

  8. Java 守护线程(Daemon) 例子

    当我们在Java中创建一个线程,缺省状态下它是一个User线程,如果该线程运行,JVM不会终结该程序.当一个线被标记为守护线程,JVM不会等待其结束,只要所有用户(User)线程都结束,JVM将终结程 ...

  9. day36 joinablequeue、多线程理论、多线程的两种使用方式、守护线程、互斥锁、死锁、递归锁、信号量

    1.joinablequeue队列 joinablequeue与queue一样,也是一种队列,其继承自queue,也有queue中的put 与get 方法,但是在joinablequeue中有自己的 ...

随机推荐

  1. python 启动pydoc查看文档

    启动pydoc查看文档 python3 -m pydoc -p 访问http://localhost:6789 或者查看官方文档:https://seleniumhq.github.io/seleni ...

  2. [CMS] UsualToolCMS-8.0 sql注入漏洞【转载】

    原文地址:http://lyscholar.cn/2019/07/30/代码审计-usualtoolcms存在sql注入漏洞/ 0x01 漏洞环境 版本信息:UsualToolCMS-8.0-Rele ...

  3. 你向 Mysql 数据库插入 100w 条数据用了多久?

    阅读本文大概需要 2 分钟. ▌目录 多线程插入(单表) 多线程插入(多表) 预处理 SQL 多值插入 SQL 事务( N 条提交一次) ▌多线程插入(单表) 问:为何对同一个表的插入多线程会比单线程 ...

  4. 线程池ScheduledThreadPoolExecutor

    Java中调度线程池ScheduledThreadPoolExecutor原理探究 一. 前言 前面讲解过Java中线程池ThreadPoolExecutor原理探究,ThreadPoolExecut ...

  5. unity序列化

    什么是序列化 unity的序列化在unity的开发中起着举重足轻的地位,许多核心的功能都是基于序列化和反序列化来实现的.序列化简单来讲就是就是将我们所要保存的数据进行二进制存储,然后当我们需要的时候, ...

  6. docker jenkins 插件安装提速

    公司安装的jenkins 自动布署服务挂了,好像有漏洞一直搞,打算重新安装一个,随便再学习一下 一上来就用docker 解决问题 #!/bin/bash docker stop myjenkins d ...

  7. snf快速开发平台试用演示地址

      BS演示地址: http://49.4.68.200:65432 administrator / Administrator     snf-cs试用版本下载地址:https://pan.baid ...

  8. 面试突击(六)——JVM如何实现JAVA代码一次编写到处运行的?

    声明:本文图片均来自网络,我只是进行了选择,利用一图胜千言的力量来帮助自己快速的回忆相关的知识点 JVM是 JAVA Virtual Machine 三个英文单词的首字母缩写,翻译成中文就是Java虚 ...

  9. MQTT研究之EMQ:【CoAP协议应用开发】

    本博文的重点是尝试CoAP协议的应用开发,其中包含CoAP协议中一个重要的开源工具libcoap的安装和遇到的问题调研.当然,为了很好的将EMQ的CoAP协议网关用起来,也调研了下EMQ体系下,CoA ...

  10. setTimeout的小尴尬

    我们都知道,alert这种内置弹框会阻塞后续代码执行: 之所以如此,就是因为JavaScript代码在浏览器中是单线程执行的.换句话说,浏览器中只有一个主线程负责运行所有JavaScript代码(不考 ...