Android 使用ScheduledThreadPoolExecutor导致BusyThreadTracer监控上报问题记录

1,398 阅读2分钟

背景

  1. 主app上通过APM对cpu消耗有一个监控,将1min内cpu占用超过30%的线程会记录上报(BusyThreadTracer)
  2. 另外还可以通过adb shell top -H -p {pid}来查看 各个线程占用的百分比

问题---推送上报有一个线程被标记为 BusyThread

从图中可以看到 push-stat-{num}线程占用都高于主线程和RenderThread的占用了,这个肯定是有问题的。找到对应的代码如下:

第一张图片的Timer线程其实没有上报,只是在看这块代码的时候发现有问题,这里直接使用Timer来实现定时任务,会导致频繁创建Thread,应该尽量避免这种方式,改用线程池的方式来实现。

主要问题在第二张和第三张图,一开始通过top命令并没有复现这个问题,后来换了个低端的手机,基本稳定复现了。注释runnable里的所有执行代码,发现还是很高,判断应该是ScheduledThreadPoolExecutor的问题。看了下源码如下:

这块地方基本都是赋值,并没有创建线程池。下面看下schedule方法:

schedule方法基本没有啥问题,说明也不是new Thread导致的问题。 后在一篇文章中发现了这是jdk的一个bug 链接

-w940

所以上述问题是ScheduledThreadPoolExecutor中的DelayedWorkQueue一直在poll 占用了cpu资源导致的。

该问题在jdk 1.9上修复了,但是测试也就在android 4.4版本有这个问题。有可能是因为android使用open jdk 可能已经偷偷修复了这个问题。

同时在developer.android.com指出尽量不要设置corePoolSize=0

关于ScheduledThreadPoolExecutor还有一个bug就是在schedule时Decreasing corePoolSize也会导致bug Decreasing ScheduledThreadPoolExecutor core pool size causes busy spin

解决方案

ScheduledThreadPoolExecutor cpu占用较高的问题已经搞清楚了,解决方案也很简单,就是设置corePoolSize>=1,避免使用corePoolSize=0的ScheduledThreadPoolExecutor。

值得提出的是,这个问题是jdk9以下的问题,因此在后端开发中也会遇到同样的问题。