合并代码后,app在登录后的一段时间偶尔会出现UI卡顿,多次尝试后发现在网络不佳的情况下更容易出现。
打开app的StrictMode开关,记录网络,磁盘的读写情况,通过日志并没有发现问题所在,在主线程中除记录了日志和读取几次数据库外并没有额外的操作。
如果有系统的anr日志就好定位了,从这个思路出发,想办法弄一个anr出来,在一台机器上弄个热点,app链接上之后,登录完成后的瞬间拔掉机器的网线,此时app的wifi链接正常,但是所有的请求都会阻塞一段时间,此办法简单有效,app出现了anr,抓取系统的anr日志:
1 | localhost:~ tc$ adb pull /data/anr/traces.txt /Users/tc/work/tmp |
在日志的前面几行就发现主线程在等待一个锁
waiting to lock <0x423e5780> … held by tid=41 (pool-1-thread-4)0x423e5780>
通过日志还原anr的过程:worker线程首先调用了m对象上的方法B,应为是synchronized方法,因此获取了m对象上的锁,一直等待网络请求完成。之后主线程也调用了m对象上的方法A,A也是synchronized的,这是它获取不到锁,一直等待B调用完成。
问题的原因是两个不相干的业务方法,使用了同一个锁来同步,就是当前实例对象,因为synchronized使用this来进行同步,所以在使用synchronized要特别注意,在已有类中添加synchronized方法时更需要注意搜索此类中是否已存在并判断是否符合业务逻辑,如果是多人协作的类,尽量使用本方法才使用的对象进行同步,另外编码的时候还需要考虑业务场景,有些并发可能只是我们自己的想象而已,在业务中是不存在的也就不需要进行同步。