logback死锁导致程序被hang住

在多线程下载文件的存储系统中,程序出现挂起情况。通过jstack分析发现,由于logback的console appender,导致了线程死锁。解决方法是移除logger配置中的console appender,避免因buffer引发的死锁现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

出问题的系统是一个存储系统,系统有一个功能就是多线程下载hadoop上的文件;上层业务方使用的是python,而公司不提供python api下载hadoop上的文件,只提供java api下载;因此我们的系统就变成了上层用户通过python调用我们的python api,我们的python api通过py4j调起jvm操作java中的对象和方法;用户在下载文件通常是100多个进程,某天用户通知我们说你们下载一个文件咋下载了一整天都没下载下来,根据日志和下载的文件发现不是因为下载文件慢,而是程序就卡住了;

分析过程

1、分析日志,跟踪程序,发现代码没啥毛病,日志也看不出来个所以然
2、jstack -l pid 堆栈跟踪 ,堆栈信息给出部分

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):

"Attach Listener" #186 daemon prio=9 os_prio=0 tid=0x00007fbf98001000 nid=0x28e8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"pool-47-thread-4" #185 prio=5 os_prio=0 tid=0x00007fbf4c44c800 nid=0x2b76 runnable [0x00007fbf348e3000]
   java.lang.Thread.State: RUNNABLE
        at java.io.FileOutputStream.writeBytes(Native Method)
        at java.io.FileOutputStream.write(FileOutputStream.java:326)
        at java.io.BufferedOutputStream.write(BufferedOutputStream.java:122)
        - locked <0x0000000080223058> (a java.io.BufferedOutputStream)
        at java.io.PrintStream.write(PrintStream.java:480)
        - locked <0x0000000080223038> (a java.io.PrintStream)
        at java.io.FilterOutputStream.write(FilterOutputStream.java:97)
        at ch.qos.logback.core.joran.spi.ConsoleTarget$1.write(ConsoleTarget.java:37)
        at ch.qos.logback.core.encoder.LayoutWrappingEncoder.doEncode(LayoutWrappingEncoder.java:131)
        at ch.qos.logback.core.OutputStreamAppender.writeOut(OutputStreamAppender.java:187)
        at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:212)
        at ch.qos.logback.core.OutputStreamAp
### Logback 死锁解决方案 Logback 中可能出现死锁的情况通常发生在配置文件解析期间或日志记录过程中。为了有效解决这些问题并防止性能下降以及便于快速定位问题[^1],建议采取以下措施: #### 配置优化 确保 `logback.xml` 或者其他形式的日志配置文件尽可能简单明了。复杂的配置不仅增加了初始化时间还可能引入潜在的风险。 对于异步追加器(Async Appender),应当合理设置缓冲区大小和阻塞策略。如果生产环境中发现由于大量无效日志输出影响到了系统表现,则应该重新评估哪些信息确实有必要被记录下来,并调整相应的阈值级别。 ```xml <configuration> <!-- 使用 AsyncAppender 来提高效率 --> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <queueSize>500</queueSize> <discardingThreshold>0</discardingThreshold> <includeCallerData>true</includeCallerData> <!-- 这里定义具体的 appender 如 Console/FILE/RollingFile 等 --> <appender-ref ref="CONSOLE"/> </appender> ... </configuration> ``` #### 日志过滤机制 通过实现自定义的 Filter 类来拦截不必要的调试信息或其他低级别的消息。这有助于减少磁盘 I/O 和内存占用的同时也使得排查错误更加容易。 ```java public class DebugLevelFilter extends Filter<ILoggingEvent> { @Override public FilterReply decide(ILoggingEvent event) { if (event.getLevel() == Level.DEBUG || event.getMessage().contains("not important")) { return FilterReply.DENY; } else { return FilterReply.ACCEPT; } } } ``` #### 锁管理 当涉及到多线程环境中的资源竞争时,采用合适的同步原语非常重要。针对高并发场景下的缓存更新操作,可以考虑使用互斥锁来进行流量控制,从而避免多个请求同时尝试修改相同的数据结构而引发的一系列连锁反应[^2]。 例如,在 Java 应用程序内部可以通过 ReentrantLock 实现基本的功能;而在分布式的架构设计里面则推荐利用 Redis 的 SETNX 命令作为分布式锁服务端点之一。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值