許多圖書館使用 ThreadLocal
變量來存儲某些線程上下文資料。通常在Web應用程式中,這個上下文是一個單一的請求(=線程)。然而,有一個警告-大多數容器使用線程池。它節省了每次執行個體化新線程的成本。
是以這兩件事都是好的,但是當組合出現問題時--如果庫不清理它的線程局部變量,它将被服務為一個包含已經填充的線程本地的線程。這方面有兩個問題:
- 記憶體洩漏-Tomcat被指控在重新部署時洩露資訊。這是因為有許多未清理的線程局部變量,并且線程池在重新部署時不會重新建立。在Tomcat 7中,通過清除所有線程局部變量并顯示警告,這是固定的。但這隻是在重新部署的時候。想象一下當線程本地實際存儲Map時的通常情況。對象被放入地圖中,如果不進行清理,它可以無限期地生長。
- 非确定性行為-庫可能期望一個幹淨的線程,是以一個空線程本地意味着“我必須做一些重要的初始化”。但相反,它被提供了一個已填充的線程本地,是以不會發生初始化,取而代之的是舊的(可能不相關的)資料。
是以,清理這些線程局部變量是非常重要的。以下是三個選擇:
-
當代碼完成它的工作時。這是一種直截了當的方法,但并不總是可能的,因為線程本地可能需要同一個請求中的庫跨多個調用。ThreadLocal.remove()
- 提供
或.cleanup()
方法-調用代碼将負責清理所有使用的資源(包括線程局部變量)。許多庫已經有了關閉IO資源的方法。.close()
- 提供一個servlet過濾器,它将在每個請求的末尾清理。這并不使庫必須依賴ServletAPI,但它改進了它在Web上下文中的使用。使用Servlet3.0,過濾器甚至可以自動注冊自己,是以這對使用者來說是不可見的。
如果是你的代碼,你就沒事了。但正如我所提到的-許多圖書館都有這個問題。是以去找他們報告吧。
https://www.jianshu.com/p/ca4a07d32af9
如果您使用拒絕針對此問題采取措施的庫,您還有另一種選擇:首先調查(通過檢視庫代碼,或者如果不是開源-反編譯)是否會發生上述兩個問題中的任何一個。(例如,如果線程局部變量僅用于存儲日期格式,則不要擔心)。如果确實存在問題,那麼自己編寫一個過濾器,清除所有線程局部變量,就像tomcat在關閉時所做的那樣。看以在這篇文章中看到
Java有兩種類型的異常--檢查和未檢查。簡而言之,選中是指開發人員可以合理地從異常中恢複的情況,而未檢查的異常是無法處理的程式設計錯誤釋何時使用哪個。
但并不是簡單的檢查異常使得代碼更加“醜陋”。它們迫使開發人員編寫TRY/CATCH塊或重新抛出異常。但是重新抛出隐藏了另一個問題--一些異常不應該跨越子產品邊界。當您被迫捕獲檢查過的異常(您不知道該如何處理)時,最常見的做法是将其包裝在RuntimeException中并重新抛出它。
https://www.douban.com/note/813760999/
實際上,它可能不是最常見的--特别是新手程式員傾向于吞下異常,其中包含空的CATCH塊。如果存在一些用于異常處理的通用層,則日志和重抛有時會導緻堆棧加倍。無論如何,這裡有許多不好的做法,導緻代碼很難調試和維護。
有人說,檢查過的例外應該完全消除,因為他們帶來的冗長,乏味和錯誤傾向。C#根本沒有檢查異常。當然,消除它們應該考慮到向後相容性。
然而,我認為,有這兩種例外的決定有其優點(ES)。這迫使開發商認為在這種情況下可能會出現例外情況,是以他必須采取措施。API聲明它将抛出異常,開發人員将看到以下内容編譯時。它加強了編譯時的安全性。您不應該等到代碼投入生産時才發現某些東西可能會失敗。Javadoc?這是一個很好的選擇,但我敢打賭,在異常發生之前,沒有人會讀javadoc。
那麼,如何擁有“最好的兩個世界”呢?我有個奇怪的想法為API定義兩個接口(通過繼承連結,這樣實際上隻支援一個接口),并通過工廠提供一種方法抛出檢查異常的實作,或者将檢查的異常包裝為未檢查異常的實作。可能是可行的,也可能是愚蠢的,我看不出來。現在看起來很奇怪。
- 編譯器仍然警告您使用的方法可能引發異常,您必須考慮處理它。
- 不必要的嘗試/捕捉不會使代碼變得醜陋。你也不會強迫你的來電者去想怎麼處理這個例外
- 吞咽例外的可能性減少了。