烂代码引发的血案
场景
以下有一段烂代码来自真实项目场景,如下:
public synchronized void savePhotos(String photoUrl,String userId){
final Photo photo = new Photo();
new Thread(new Runnable() {
@Override
public void run() {
//赋值
photo.setUrl(photoUrl);
photo.setUserId(userId);
//保存图片信息入库
photoService.save(photo);
//下载图片到本地服务器
photoService.downloadFileByUrl(photoUrl);
}
}).start();
public synchonized void downloadFilesByUrl(String photoUrl) {
........
httpClient.execute();//执行远程调用
......
}
这个savePhoto方法主要功能是保存图片信息到数据库并且下载图片到本地服务器,用户端每次请求当前服务,都会调用一次savePhoto方法(用户每次请求,当前容器都会启动一条线程去处理)。
注意:photoService.downloanFileByUrl(photoUrl)里面使用到httpClient,没有设置超时时间时,有可能出现无限等待,并且downloanFileByUrl方法上也加了synchonized关键字,内部也没有再开启其他线程,直接就是远程下载图片的操作
问题:
1.请列举该代码可能出现的隐患?
2.假设执行photoService.downloanFileByUrl 方法时,由于该方法下载图片时出现异常,并且该下载方法没有设置超时,导致了一直处于超时无响应,那么会发生什么问题?
3.通过系统监控可以看到此时线程数不断上升,假设你从来没有接触过这个项目,请提出一个方案去排查引起该问题的原因。
解决思路:
1.对于第一题,出现的隐患,首先从方法第一行开始观察
synchonized关键字是否必要?
方法内是否存在共享变量?
是否有必要每次创建一条线程去处理?
当downloanFileByUrl挂起时,会出现什么问题?
2.对于第二题解决思路
从synchonized关键字入手,该关键字会把整个方法锁住,所有线程都会等待(但是对于方法内部开启线程的情况,不会引起阻塞)
3.对于第三题解决思路
想想分析线程的工具有哪些
个人思路:
1.对第一题,有以下隐患:
当前方法并没有任何共享变量,synchonized会导致整个方法锁住,同一时间只允许一条线程进入,其他线程都会等待,当并发量大时会影响系统吞吐(但上面的代码synchonized关键字对于savePhotos方法不会影响吞吐,因为其内部单独又开启了线程),synchonized关键字可以直接去掉
当调用photoService.downloadFileByUrl(photoUrl)方法时,因为每次都new Thread,这样会导致每次请求都会创建一条线程去处理,而每一条线程都会阻塞在下载图片的方法,当某一次调用其底层使用的httpClient下载图片挂起时,当前该线程会阻塞在downloadFileByUrl(photoUrl)里不能释放资源(因为有synchonized关键字,并且其内部没有再开启线程),这会导致后续所以创建的线程都会阻塞在该方法,从而引起线程膨胀,最终造成系统宕机,如果确认downloadFileByUrl里没有共享成员变量,需要把downloadFileByUrl方法上synchonized去掉,因为它将会是引起线程阻塞的万恶之源
每次请求都new Thread也会造成线程资源浪费,可以考虑使用线程池代替
2.对第二题,可能会出现以下问题:
所有线程阻塞在downloadFileByUrl方法上,线程数不断上升,从而导致宕机
3.对于第三题,可以使用如下方案去排查:
a. 第一步先找出Java进程ID,可通过如下命令
ps -ef | grep 应用名 | grep -v grep
b. 假设当前进程ID是2110,再利用jstack分析该PID,很快我们就找到了一个线程处于WAITING状态,查看堆栈信息,找到对应的代码即可
jstack 2110
也可以通过ps -Lfp pid或者ps -mp pid -o THREAD, tid, time或者top -Hp pid去找出消耗CPU最高的线程,假设线程ID是21742,然后使用printf “%x ” 21742,打印其16进制数值为54ee(因此jstack线程日志nid用16进制表示),再通过 jstack 2110 | grep 54ee 就可以找到最消耗CPU的线程堆栈信息