1、以下方式存在的問題程序員
new Thread() { @Override public void run() { // 業務邏輯 } }.start();
一、首先頻繁的建立、銷燬對象是一個很消耗性能的事情;數據庫
二、若是用戶量比較大,致使佔用過多的資源,可能會致使咱們的服務因爲資源不足而宕機;服務器
三、綜上所述,在實際的開發中,這種操做實際上是不可取的一種方式。app
2、使用線程池有什麼優勢ide
一、線程池中線程的使用率提高,減小對象的建立、銷燬;性能
二、線程池能夠控制線程數,有效的提高服務器的使用資源,避免因爲資源不足而發生宕機等問題;測試
3、線程池的四種使用方式spa
一、newCachedThreadPool線程
建立一個線程池,若是線程池中的線程數量過大,它能夠有效的回收多餘的線程,若是線程數不足,那麼它能夠建立新的線程。設計
public static void method() throws Exception { ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { final int index = i; Thread.sleep(1000); executor.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " " + index); } }); } }
執行結果
經過分析我看能夠看到,至始至終都由一個線程執行,實現了線程的複用,並無建立多餘的線程。
若是當咱們的業務須要必定的時間進行處理,那麼將會出現什麼結果。咱們來模擬一下。
能夠明顯的看出,如今就須要幾條線程來交替執行。
不足:這種方式雖然能夠根據業務場景自動的擴展線程數來處理咱們的業務,可是最多須要多少個線程同時處理缺是咱們沒法控制的;
優勢:若是當第二個任務開始,第一個任務已經執行結束,那麼第二個任務會複用第一個任務建立的線程,並不會從新建立新的線程,提升了線程的複用率;
二、newFixedThreadPool
這種方式能夠指定線程池中的線程數。舉個栗子,若是一間澡堂子最大隻能容納20我的同時洗澡,那麼後面來的人只能在外面排隊等待。若是硬往裏衝,那麼只會出現一種情景,摩擦摩擦...
首先測試一下最大容量爲一個線程,那麼會不會是咱們預測的結果。
public static void method_01() throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(1); for (int i = 0; i < 10; i++) { Thread.sleep(1000); final int index = i; executor.execute(() -> { try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " " + index); }); } executor.shutdown(); }
執行結果
咱們改成3條線程再來看下結果
優勢:兩個結果綜合說明,newFixedThreadPool的線程數是能夠進行控制的,所以咱們能夠經過控制最大線程來使咱們的服務器打到最大的使用率,同事又能夠保證及時流量忽然增大也不會佔用服務器過多的資源。
三、newScheduledThreadPool
該線程池支持定時,以及週期性的任務執行,咱們能夠延遲任務的執行時間,也能夠設置一個週期性的時間讓任務重複執行。 該線程池中有如下兩種延遲的方法。
scheduleAtFixedRate
測試一
public static void method_02() { ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { long start = new Date().getTime(); System.out.println("scheduleAtFixedRate 開始執行時間:" + DateFormat.getTimeInstance().format(new Date())); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } long end = new Date().getTime(); System.out.println("scheduleAtFixedRate 執行花費時間=" + (end - start) / 1000 + "m"); System.out.println("scheduleAtFixedRate 執行完成時間:" + DateFormat.getTimeInstance().format(new Date())); System.out.println("======================================"); } }, 1, 5, TimeUnit.SECONDS); }
執行結果
測試二
總結:以上兩種方式不一樣的地方是任務的執行時間,若是間隔時間大於任務的執行時間,任務不受執行時間的影響。若是間隔時間小於任務的執行時間,那麼任務執行結束以後,會立馬執行,至此間隔時間就會被打亂。
scheduleWithFixedDelay
測試一
public static void method_03() { ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { long start = new Date().getTime(); System.out.println("scheduleWithFixedDelay 開始執行時間:" + DateFormat.getTimeInstance().format(new Date())); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } long end = new Date().getTime(); System.out.println("scheduleWithFixedDelay執行花費時間=" + (end - start) / 1000 + "m"); System.out.println("scheduleWithFixedDelay執行完成時間:" + DateFormat.getTimeInstance().format(new Date())); System.out.println("======================================"); } }, 1, 2, TimeUnit.SECONDS); }
執行結果
測試二
public static void method_03() { ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { long start = new Date().getTime(); System.out.println("scheduleWithFixedDelay 開始執行時間:" + DateFormat.getTimeInstance().format(new Date())); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } long end = new Date().getTime(); System.out.println("scheduleWithFixedDelay執行花費時間=" + (end - start) / 1000 + "m"); System.out.println("scheduleWithFixedDelay執行完成時間:" + DateFormat.getTimeInstance().format(new Date())); System.out.println("======================================"); } }, 1, 2, TimeUnit.SECONDS); }
執行結果
總結:一樣的,跟scheduleWithFixedDelay測試方法同樣,能夠測出scheduleWithFixedDelay的間隔時間不會受任務執行時間長短的影響。
四、newSingleThreadExecutor
這是一個單線程池,至始至終都由一個線程來執行。
public static void method_04() { ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 5; i++) { final int index = i; executor.execute(() -> { try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " " + index); }); } executor.shutdown(); }
執行結果
4、線程池的做用
線程池的做用主要是爲了提高系統的性能以及使用率。文章剛開始就提到,若是咱們使用最簡單的方式建立線程,若是用戶量比較大,那麼就會產生不少建立和銷燬線程的動做,這會致使服務器在建立和銷燬線程上消耗的性能可能要比處理實際業務花費的時間和性能更多。線程池就是爲了解決這種這種問題而出現的。
歡迎你們關注個人公種浩【程序員追風】,文章都會在裏面更新,整理的資料也會放在裏面。
一樣思想的設計還有不少,好比數據庫鏈接池,因爲頻繁的鏈接數據庫,然而建立鏈接是一個很消耗性能的事情,全部數據庫鏈接池就出現了。
最後
歡迎你們一塊兒交流,喜歡文章記得點個贊喲,感謝支持!