閑話少說,
Tomcat日志報錯及堆棧信息:
因為這個問題,掛了幾個節點。
跟蹤報錯的代碼發現代碼中用到future.get()方法:
Callable<Object> myCallable = createThreadCallable(interfaceName, httpbody, authSeedVo); Future<Object> future = threadPool.submit(myCallable); try { super.sendJson(future.get().toString()); } catch (Exception e) { logger.error("delsub", "獲取子線程時發生異常", e); }
任務由線程池提供的線程執行,那么這時候主線程則會阻塞,直到任務線程喚醒它們。獲取結果時,通過future.get()方法獲取:
當 s<= COMPLETING時,表明任務仍然在執行且沒有被取消。如果它為true,那么走到awaitDone方法。
下面來看看awaitDone方法里面干了啥:
/** * Awaits completion or aborts on interrupt or timeout. * * @param timed true if use timed waits * @param nanos time to wait, if timed * @return state upon completion */
private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet
Thread.yield(); else if (q == null) q = new WaitNode(); else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); } }
一屏截圖不完整,主要截圖引起阻塞的判斷條件:
如果給定的時間是非0(負數)或者給定的時間(正數, 時間單位時毫秒)已經過去了(0的時候會一直阻塞着)
此處因為網絡或者其他原因,導致主線程調用future.get()方法時,任務子線程還沒有從對接的第三方獲取到數據,致使子線程掛起(等待喚醒),從而一直消耗線程池中的資源。
建議修改方案:去掉多線程操作;或者調用future.get(long timeout, TimeUnit unit)方法;
因為當任務正常結束或者異常時,都會調用finishCompletion去喚醒等待線程。
查看官方文檔,尋找park方法:
字面理解park,就算占住,停車的時候不就把這個車位給占住了么?起這個名字還是很形象的。unpark,占住的反義詞,就是釋放。把車從車位上開走。
翻譯一下:
- park:阻塞當前線程,(1)當配對的unpark發生或者(2)配對的unpark已經發生或者線程被中斷時恢復(unpark先行,再執行park)。 (3)當absolute是false時,如果給定的時間是非0(負數)或者給定的時間(正數, 時間單位時毫秒)已經過去了(0的時候會一直阻塞着)。(4)當Absolute是true時,如果給定的時間(時間單位是納秒)過去了或者偽造的(在我理解是參數不合法時)線程會恢復中斷。這個操作是不安全的,所以在其他調用會很奇怪(奇怪?反正就是用的時候要小心)
- unpark:當指定線程被park命令阻塞時unpark命令可以恢復阻塞。在park命令沒有被先調用過的時候,調用unpark,線程仍然不被阻塞。
參考文章:
java.util.concurrent.locks.LockSupport類詳解:https://my.oschina.net/readjava/blog/282882
futureTask:http://www.cnblogs.com/maypattis/p/5827671.html