現象: 當前項目啓動一段時間,有一個服務致使CPU使用率持續超過30%算法
環境:Windows 7, CPU: 8核, 內存: 8g內存安全
定位過程:線程
啓動項目,查看Java進程ID排序
查看Event Processor 的CPU使用狀況,此時基本維持在1%左右:隊列
開啓Simulator發送幾包數據,再次查看,發現CPU已經飈上去了:進程
關閉Simulator,等待數據都處理完畢,再次查看,CPU並未降下去:內存
使用ProcessExplorer查看Event Processor線程的CPU使用狀況,發現線程40160和40156的CPU佔用率持續很高,資源
使用jstack -l 35684 打印出Event Process的線程棧,查看線程ID(需轉化成16進制)對應(40160-> 9CE0, 40156-> 9CDC)的線程棧:it
能夠看出,線程TaskScheduler_com.delta.atm.services.impl.AutoTTExecutorServiceImpl和TaskScheduler_com.delta.atm.services.impl.AlarmExecutorServiceImpl處於運行狀態,持續打印線程信息發現這兩個線程一直處於RUNNABLE的狀態,此時並未開啓simulator,此線程應該處於睡眠纔對。效率
先走讀代碼,查看AlarmServiceImpl,
再查看SchedulerServiceImpl,
從下面代碼的角度看,在沒有數據的狀況下takeTask應該只會執行一次,而後休眠,等待喚醒。
根據打印的線程棧發現,這兩個線程都長時間運行在takeTask方法,因此猜想這個部分有死循環,並且因爲此機器是8核,1/8=0.125,若是有兩個線程持續While循環,則基本就會佔用25%的CPU資源。
加上輸出,打印出全局的count,找到緣由了,線程睡眠的條件是count爲0才睡眠,可是數據都處理完了打印出來的count不爲0因此線程會一直空轉,
可是爲何會出現數據處理完count還不爲0的狀況呢?
從代碼結果來看alarmService每收到一包數據count就會加1,每取一包數據,count就會減1,並且count是AtomicInteger線程安全類型。
因爲alarmService用的是一個生產者-消費者模型,大概結構以下圖所示,須要一個全局的count來表示全部queue裏面的數據,當count數量爲0的時候Task Schedule則睡眠,不然會持續調度。
查看當前queue裏面的數據是空的,說明queue裏面的數據已經處理完了,可是count的值卻不是0。
查看下面的代碼找到緣由,這個queue是咱們本身封裝的,每一個site對應一個queue,key值是dataTime,這個值是根據印度的box取的且只有10位,精確到秒,可是simulator每一個站點數據的發送頻率常常會一秒鐘發送多包數據,因此就會有重複的dataTime,也就形成了queue裏面數據被覆蓋了,這個時候queue的總大小就和記錄的count不匹配了。
其實這個queue的內部咱們使用的是一個treeMap,因此不容許key值重複,最好的辦法是重寫一個容許key值重複而且可排序的一個隊列,可是考慮到現實環境中每一個站點3分鐘才發一包數據不可能出現每秒多包數據的狀況,並且TreeMap內部用的是紅黑樹效率挺高的,若是本身寫排序算法效率可能不會太好,因此暫時使用判斷作規避,若是同一個站點queue裏面已經有相同的dataTime則直接忽略這一包數據。
從新打包啓動查看輸出, count已經降下來了
查看CPU使用率以下
查看線程狀態,已經處於waiting,問題解決。