每篇一句
你如果不是富二代,你要努力讓你的兒子成為富二代
說在前面
線程池關閉的意義不僅僅在於結束線程執行,避免內存溢出,因為大多使用的場景並非上述示例那樣 朝生夕死。線程池一般是持續工作的全局場景,如數據庫連接池。
我之前看到很多同事寫代碼,為了提高效率,采用多線程去優化。由為了提高多線程的性能,用到了線程池。
表面上看起來很高大上了,但其實上發現很多人用到了局部變量的線程池,然后使用過后並沒有回收,導致了線程泄漏甚至內存溢出。
Executors作為局部變量時,創建了線程,一定要記得調用executor.shutdown();來關閉線程池,如果不關閉,會有線程泄漏問題。
實例模擬
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 4 public class TestThread { 5 6 public static void main(String[] args) { 7 while (true) { 8 try { 9 ExecutorService service = Executors.newFixedThreadPool(1); 10 service.submit(new Runnable() { 11 public void run() { 12 try { 13 Thread.sleep(2000); ////模擬處理業務 14 } catch (InterruptedException e) { 15 } 16 } 17 }); 18 service = null; 19 } catch (Exception e) { 20 } 21 try { 22 Thread.sleep(2000); 23 } catch (InterruptedException e) { 24 } 25 } 26 } 27 28 }
運行后,查看jvm,會發現線程每2秒就增長一個。
加了shutdown代碼后
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 4 public class TestThread { 5 6 public static void main(String[] args) { 7 while (true) { 8 ExecutorService service = Executors.newFixedThreadPool(1); 9 try { 10 service.submit(new Runnable() { 11 public void run() { 12 try { 13 Thread.sleep(2000); 14 } catch (InterruptedException e) { 15 } 16 } 17 }); 18 } catch (Exception e) { 19 }finally{ 20 service.shutdown(); 21 } 22 try { 23 Thread.sleep(2000); 24 } catch (InterruptedException e) { 25 } 26 } 27 } 28 }
就一直很平穩
再看個例子
public static void main(String[] args) throws Exception { //用於獲取到本java進程,進而獲取總線程數 RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); String jvmName = runtimeBean.getName(); System.out.println("JVM Name = " + jvmName); long pid = Long.valueOf(jvmName.split("@")[0]); System.out.println("JVM PID = " + pid); ThreadMXBean bean = ManagementFactory.getThreadMXBean(); int n = 1000; for (int i = 0; i < n; i++) { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); for (int j = 0; j < 5; j++) { executor.execute(() -> { System.out.println("當前線程總數為:" + bean.getThreadCount()); }); } // executor.shutdown(); } Thread.sleep(10000); System.out.println("線程總數為 = " + bean.getThreadCount()); }
這里ThreadPoolExecutor
作為局部變量,若你不手動關閉:最后一句輸出為
1 線程總數為 = 5006
也就是說:線程全部泄漏(一個線程都沒有死,沒有被回收),白白的浪費了內存。這個在內存吃緊的時候容易造成死機。
那么加上executor.shutdown()
這句,手動去給它關閉呢?最終打印:
1 線程總數為 = 6
可見,效果是非常好的。因此:局部線程池,請務必務必要手動關閉。
注意:還有個區別是若你沒有shutdonw,那么最終主線程是不會終止的。而如果你shutdown了,主線程跑完也就終止了。
最后說明
此處用的newFixedThreadPool(1)來模擬業務創建,但是勿噴。實際情況中一般不會創建只有一個線程的線程池,這里只是表達一個意思即可。
希望大家能夠舉一反三。
線程池設置多大合適呢
雖然線程池大小的設置受到很多因素影響,但是這里給出一個參考公式:
最佳線程數目 = ((線程等待時間+線程CPU時間)/線程CPU時間 )* CPU數目
比如平均每個線程CPU運行時間為0.5s,而線程等待時間(非CPU運行時間,比如IO)為1.5s,CPU核心數為8,那么根據上面這個公式估算得到:((0.5+1.5)/0.5)*8=32。這個公式進一步轉化為:
最佳線程數目 = (線程等待時間與線程CPU時間之比 + 1)* CPU數目
線程等待時間所占比例越高,需要越多線程。線程CPU時間所占比例越高,需要越少線程。
所以並不是單純的只是配一個CUP核心數就ok了。但一般都是整數倍。
轉載於:https://blog.csdn.net/f641385712/article/details/82021919