【小家Java】自定義的線程池需要關閉嗎?(局部變量Executors線程池一定要手動關閉)


每篇一句

你如果不是富二代,你要努力讓你的兒子成為富二代

說在前面

線程池關閉的意義不僅僅在於結束線程執行,避免內存溢出,因為大多使用的場景並非上述示例那樣 朝生夕死。線程池一般是持續工作的全局場景,如數據庫連接池。

我之前看到很多同事寫代碼,為了提高效率,采用多線程去優化。由為了提高多線程的性能,用到了線程池。

表面上看起來很高大上了,但其實上發現很多人用到了局部變量的線程池,然后使用過后並沒有回收,導致了線程泄漏甚至內存溢出。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM