1. 程式人生 > >java執行緒池監控

java執行緒池監控

原因

最近在完善公司的基礎釋出平臺的時候,使用到了一執行緒去做一些非同步的事情,在開發環境和測試環境驗證沒有任何問題,但是在程式在生產執行一段時間後,發現沒有得到自己想要的結果,為此開始了漫長的排查bug的之路,因為用到了一些執行緒,但是實際又沒有對這些執行緒足夠的監控,所以在排查問題的時候也是歷經艱難險阻;

 

原始程式碼

 
 

protected ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);


/**
* 同步應用的jenkins狀態
*/
public void threadASyncAppJenkinsStatus() {
executorService.scheduleAtFixedRate(() -> {
List<ArchitectureApp> architectureApps = architectureAppMapper.listArchitectureApp();
architectureApps.parallelStream().forEach(architectureApp -> syncJenkinsBuild(architectureApp.getName(), ArchitectureType.APP));

}, 0, 6, TimeUnit.SECONDS);

}

/**
* 同步元件的jenkins狀態
*/
public void syncComponentJenkinsStatus() {
executorService.scheduleAtFixedRate(() -> {
List<ArchitectureComponent> architectureComponents = architectureComponentMapper.listArchitectureComponent();
architectureComponents.parallelStream().forEach(architectureComponent -> syncJenkinsBuild(architectureComponent.getName(), ArchitectureType.COMPONENT));
}, 0, 6, TimeUnit.SECONDS);

}
 

這是其中一部分的程式碼,做的事情很簡單,程式每隔6s就去輪詢元件和應用的狀態,然後後面我會通過websocket同步到前端頁面。這是一段很簡單的程式碼,很難想象這段程式碼可能出錯。但是事與願違,通過開發和測試環境的測試,在上到生產運行了兩天發現前端頁面的jenkins狀態並沒有同步。而通過檢視日誌,也沒法觀察問題出在哪,所以只能另尋他法;

 

ExecutorsMonitor執行緒監控類

以下是我們開發的一個執行緒池工具類,該工具類擴充套件ScheduledThreadPoolExecutor實現了執行緒池監控功能,能實時將執行緒池使用資訊列印到日誌中,方便我們進行問題排查、系統調優。具體程式碼如下

@Slf4j
class ExecutorsMonitor extends ScheduledThreadPoolExecutor {


    private ConcurrentHashMap<String, Date> startTimes;
    private String poolName;

    /**
     * 呼叫父類的構造方法,並初始化HashMap和執行緒池名稱
     *
     * @param corePoolSize 執行緒池核心執行緒數
     * @param poolName     執行緒池名稱
     */
    public ExecutorsMonitor(int corePoolSize, String poolName) {
        super(corePoolSize);
        this.startTimes = new ConcurrentHashMap<>();
        this.poolName = poolName;
    }

    /**
     * 執行緒池延遲關閉時(等待執行緒池裡的任務都執行完畢),統計執行緒池情況
     */
    @Override
    public void shutdown() {
        super.shutdown();
    }

    /**
     * 執行緒池立即關閉時,統計執行緒池情況
     */
    @Override
    public List<Runnable> shutdownNow() {
        return super.shutdownNow();
    }

    /**
     * 任務執行之前,記錄任務開始時間
     */
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        startTimes.put(String.valueOf(r.hashCode()), new Date());
    }

    /**
     * 任務執行之後,計算任務結束時間
     */
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
        Date finishDate = new Date();
        long diff = finishDate.getTime() - startDate.getTime();
        // 統計任務耗時、初始執行緒數、核心執行緒數、正在執行的任務數量、已完成任務數量、任務總數、佇列裡快取的任務數量、池中存在的最大執行緒數、最大允許的執行緒數、執行緒空閒時間、執行緒池是否關閉、執行緒池是否終止
        log.info(String.format(this.poolName
                        + "-pool-monitor: Duration: %d ms, PoolSize: %d, CorePoolSize: %d, Active: %d, Completed: %d, Task: %d, Queue: %d, LargestPoolSize: %d, MaximumPoolSize: %d,  KeepAliveTime: %d, isShutdown: %s, isTerminated: %s",
                diff, this.getPoolSize(), this.getCorePoolSize(), this.getActiveCount(), this.getCompletedTaskCount(), this.getTaskCount(),
                this.getQueue().size(), this.getLargestPoolSize(), this.getMaximumPoolSize(), this.getKeepAliveTime(TimeUnit.MILLISECONDS),
                this.isShutdown(), this.isTerminated()));
    }

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, String poolName) {
        return new ExecutorsMonitor(corePoolSize, poolName);
    }

}

  

後來在生產終於定位問題,發現執行緒內部後來停止,同時發現的還有報錯,通過查閱資料發現,原來執行緒發生異常後會退出,通過try catch很好的解決了這個問題