JAVA 几百线程等待一把无人持有的锁

问题描述

最近有两台服务器经常出现此问题。 操作系统分别为centos7.9 和 centos6.10, jdk1.7.0_80, tomcat8.5。

现象为: cpu负载降低但还是有一定负载, 带宽使用明显下降, jvm并没有卡死,还是有部分业务能执行, 堆内存使用速度明显变慢, gc频率降低。

通过关联的线程栈分析,可以看到有370个线程在等待 java.util.IdentityHashMap 这把锁。 但是没有线程持有些锁。
需要说明的是:这几次发生此现象的时候锁的地方不固定。 有时候是hibernate的getCache, 有时候是SSL相关的,还有其它的。

我的认知当中,正常情况下,至少会有一个线程持人锁, 才会导致其它线程等待, 这种无人持有的情况完全超出了理解范围。
这里不知道怎么上传线程dump文件, 我上传到其它地方了:线程栈详情

问题出现的环境背景及自己尝试过哪些方法

机器配置: 阿里云ECS。 8核32G。
运行环境: centos7.9, centos6.10, jdk1.7.0_80, tomcat8.5
jvm 选项: -server -Xms12g -Xmx12g -XX:PermSize=256m -XX:MaxPermSize=256m -Djava.library.path=/usr/local/apr/lib
应用主要是跑定时任务的,有很多http请求,还有大量图片处理操作,因为调用外网api和图片很多, 所以线程耗时都比较长,FullGC 一般10分钟左右就会进行一次,一次耗200ms左右的时间。

相关代码

同步块代码是Druid连接池的com.alibaba.druid.pool.DruidDataSource.getConnectionDirect方法, 同步块处代码为:

            if (isRemoveAbandoned()) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                poolableConnection.setConnectStackTrace(stackTrace);
                poolableConnection.setConnectedTimeNano();
                poolableConnection.setTraceEnable(true);

                synchronized (activeConnections) {
                    activeConnections.put(poolableConnection, PRESENT);
                }
            }

你期待的结果是什么?实际看到的错误信息又是什么?

希望大家帮我分析下出现这种情况的原因。

阅读 1.9k
1 个回答

这可能跟锁没有关系,我看了ThreadPoolTaskExecutor这个线程池,其中有一部分TIMED_WAIT状态的线程,stacktrace中有这样的信息:
at
java.util.concurrent.FutureTask.get(FutureTask.java:199 )
146
at
com.tb.util.thread.ThreadPoolTaskExecutorUtils.executeSubmit(ThreadPoolTaskExecutorUtils.java:94 )
146
at
com.tb.util.thread.ThreadPoolTaskExecutorUtils.submit(ThreadPoolTaskExecutorUtils.java:70 )
146
我推测这块的代码是当线程池中的某个线程在执行过程中,需要一个子线程来做异步,主线程等待子线程执行结果后再继续执行,但这个子线程任务却也提交给了同一个线程池。这样用线程池是有问题的,因为在线程池没有空闲线程时任务会先提交的队列中,等待有空闲线程再去队列中取任务执行。放在这个场景里,就是主线像线程池中提交一个子任务进入队列排队,然后主线程在子任务完成前一直阻塞,而子任务则在线程池队列中等待主线程执行完成,因此形成了死锁的现象。你可以排查一下是否存在此问题,如果是这个问题那么验证也是很容易的,只要不断增加并发量,达到一定程度就一定会出问题

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏