3 回答

TA貢獻1827條經驗 獲得超8個贊
最安全的做法是使用具有最大線程數量的應用程序寬線程池,以便在必要時將任務排隊。這ExecutorService對此很有幫助。
在應用程序啟動或servlet初始化時,使用Executors類:
executor = Executors.newFixedThreadPool(10); // Max 10 threads.
然后在servlet的服務期間(你可以忽略你不感興趣的情況的結果):
Future<ReturnType> result = executor.submit(new CallableTask());
最后,在應用程序關閉或servlet的銷毀期間:
executor.shutdownNow(); // Returns list of undone tasks, for the case that.

TA貢獻1803條經驗 獲得超3個贊
您可以使用像Foo-CommonJ這樣的CommonJ WorkManager(JSR 237)實現:
CommonJ - JSR 237定時器和工作管理器
Foo-CommonJ是一個JSR 237 Timer和WorkManager實現。它被設計用于沒有自己實現的容器 - 主要是像Tomcat這樣的普通servlet容器。它還可以用于沒有WorkManager API或具有JBoss等非標準API的完全成熟的Java EE應用程序服務器。
為什么使用WorkManagers?
常見的用例是Servlet或JSP需要聚合來自多個源的數據并在一個頁面中顯示它們。像J2EE容器那樣對自己的線程進行托管環境是不合適的,不應該在應用程序級代碼中完成。在這種情況下,WorkManager API可用于并行檢索數據。
安裝/部署CommonJ
JNDI資源供應商的部署依賴。此實現附帶一個實現javax.naming.spi.ObjectFactory 接口的Factory類, 使其可以在最流行的容器中輕松部署。它也可以作為JBoss服務使用。更多...
更新:只是為了澄清,以下是Java EE預覽的并發實用程序(看起來像是JSR-236和JSR-237的后續版本)寫的關于非托管線程的內容:
2.1容器管理與非管理線程
Java EE應用程序服務器需要資源管理,以便集中管理并保護應用程序組件不會消耗不必要的資源。這可以通過匯集資源和管理資源的生命周期來實現。使用Java SE并發實用程序(如 java.util.concurrencyAPI) java.lang.Thread以及 java.util.Timer服務器應用程序組件(如servlet或EJB)是有問題的,因為容器和服務器不了解這些資源。
通過擴展 java.util.concurrentAPI, 應用程序服務器和Java EE容器可以了解所使用的資源,并為運行的異步操作提供正確的執行上下文。
這主要通過提供主要java.util.concurrent.ExecutorService 接口的托管版本來 實現。
所以沒有新的IMO,“舊”問題是一樣的,非托管線程仍然是非托管線程:
它們對于應用程序服務器是未知的,并且無法訪問Java EE上下文信息。
他們可以使用應用程序服務器背面的資源,并且沒有任何管理能力來控制其數量和資源使用情況,這可能會影響應用程序服務器從故障中恢復資源或正常關閉的能力。

TA貢獻1998條經驗 獲得超6個贊
我知道這是一個老問題,但人們一直在問它,試圖做這種事情(在處理servlet請求時顯式產生線程)一直......這是一個非常有缺陷的方法 - 出于多個原因。 ..簡單地說Java EE容器對這種做法不滿意是不夠的,盡管一般都是......
最重要的是,人們永遠無法預測servlet在任何給定時間將接收多少并發請求。根據定義,Web應用程序,servlet意味著能夠一次處理給定端點上的多個請求。如果您正在編程請求處理邏輯以顯式啟動一定數量的并發線程,那么您可能面臨一個完全不可避免的情況,即可用線程耗盡并阻塞您的應用程序。您的任務執行程序始終配置為使用限制為有限合理大小的線程池。大多數情況下,它不大于10-20(你不需要太多線程執行你的邏輯 - 取決于任務的性質,他們競爭的資源,服務器上的處理器數量等)讓我們說,你的請求處理程序(例如 MVC控制器方法)調用一個或多個@Async-annotated方法(在這種情況下,Spring抽象任務執行程序并使事情變得簡單)或明確使用任務執行程序。當您的代碼執行時,它開始從池中獲取可用的線程。如果您總是一次處理一個請求而沒有立即的后續請求,那就沒問題。(在這種情況下,您可能正在嘗試使用錯誤的技術來解決您的問題。)但是,如果它是一個Web應用程序,它暴露給任意(甚至已知)客戶端可能正在通過請求錘擊端點,您將快速耗盡線程池,請求將開始堆積,等待線程可用。僅僅因為這個原因,你應該意識到你可能走錯了路 - 如果你正在考慮這樣的設計。當您的代碼執行時,它開始從池中獲取可用的線程。如果您總是一次處理一個請求而沒有立即的后續請求,那就沒問題。(在這種情況下,您可能正在嘗試使用錯誤的技術來解決您的問題。)但是,如果它是一個Web應用程序,它暴露給任意(甚至已知)客戶端可能正在通過請求錘擊端點,您將快速耗盡線程池,請求將開始堆積,等待線程可用。僅僅因為這個原因,你應該意識到你可能走錯了路 - 如果你正在考慮這樣的設計。當您的代碼執行時,它開始從池中獲取可用的線程。如果您總是一次處理一個請求而沒有立即的后續請求,那就沒問題。(在這種情況下,您可能正在嘗試使用錯誤的技術來解決您的問題。)但是,如果它是一個Web應用程序,它暴露給任意(甚至已知)客戶端可能正在通過請求錘擊端點,您將快速耗盡線程池,請求將開始堆積,等待線程可用。僅僅因為這個原因,你應該意識到你可能走錯了路 - 如果你正在考慮這樣的設計。)但是,如果它是一個Web應用程序,它暴露給任意(甚至已知)客戶端可能正在通過請求錘擊端點,您將很快耗盡線程池,并且請求將開始堆積,等待線程成為可用。僅僅因為這個原因,你應該意識到你可能走錯了路 - 如果你正在考慮這樣的設計。)但是,如果它是一個Web應用程序,它暴露給任意(甚至已知)客戶端可能正在通過請求錘擊端點,您將很快耗盡線程池,并且請求將開始堆積,等待線程成為可用。僅僅因為這個原因,你應該意識到你可能走錯了路 - 如果你正在考慮這樣的設計。
一個更好的解決方案可以是階段性的數據要被處理異步(這可能是一個隊列,或任何其它類型的臨時/分段數據存儲器),并返回該響應。擁有一個外部獨立應用程序,甚至是它的多個實例(部署在Web容器外部)輪詢登臺端點并在后臺處理數據,可能使用有限數量的并發線程。這樣的解決方案不僅可以為您提供異步/并發處理的優勢,而且還可以擴展,因為您可以根據需要運行此類輪詢器的多個實例,并且可以分配它們,指向登臺端點。HTH
添加回答
舉報