多線程實現方式及區別
為什麽要用線程池?
- 單獨創建線程的缺點:
a. 每次new Thread新建對象性能差。
b. 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統資源導致死機或oom。
c. 缺乏更多功能,如定時執行、定期執行、線程中斷。
- 創建線程池的優點:
a. 重用存在的線程,減少對象創建、消亡的開銷,性能佳。
b. 可有效控制最大並發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
c. 提供定時執行、定期執行、單線程、並發數控制,指定隊列大小,失敗策略等功能,可以根據我們項目的需要自定義創建不同功能的線程池。
兩種創建方式
- Executors創建方式
- Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
示例:
package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (inti = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { public void run() { System.out.println(index); } }); } } }
說明:創建一個可緩存線程池,應用中存在的線程數可以無限大。當提交任務速度高於線程池中任務處理速度時,緩存線程池會不斷的創建線程,線程數完全取決於jvm可以創建的線程數,直到資源耗盡會拋出oom異常;如果在執行第二個任務時,第一個任務已經完成,那麽會復用第一個任務的線程執行第二個任務,如果一個線程超過60秒沒有被使用,就會被線程池回收。所以大家要慎用此方法,適用於提交短期的異步小程序,以及負載較輕的服務器。
- Executors.newFixedThreadPool(2)
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
示例:
package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }View Code
說明:創建一個線程數固定的線程池,可以根據系統的資源進行設置,如:Runtime.getRuntime().availableProcessors()。線程不會被回收,除非調用shutdown()方法才會回收,如果某個線程因為執行異常而結束,那麽線程池會補充一個新線程。此方法可控制線程最大並發數,超出線程數據的任務會放到隊列中,但是從源碼中可以看到此隊列為無界隊列,所以如果我們一次查出的數據過多很有可能會導致oom異常,因為隊列會無限擴充。使用於為了滿足資源管理需求而需要限制當前線程數量的場合,使用於負載比較重的服務器但是要註意控制任務數量。
- Executors.newScheduledThreadPool(2)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}
說明:計劃型線程池,可以設置固定時間的延時或者定期執行任務,多數情況下可用來替代Timer類,同樣是看線程池中有沒有空閑線程,如果有,直接拿來使用,如果沒有,則新建線程加入池。使用的是 DelayedWorkQueue 作為等待隊列,這中類型的隊列會保證只有到了指定的延時時間,才會執行任務。
- Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())); }
說明:創建一個單線程的線程池,它只會用唯一的工作線程來執行任務,也就相當於單線程串行執行任務。如果這個唯一的線程因為異常結束,那麽會有一個新的線程來替代它。保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。使用 LinkedBlockingQueue 作為等待隊列。
- Executors.newSingleThreadScheduledExecutor();
說明:創建一個單例線程池,定期或延時執行任務。
- Executors.newWorkStealingPool(3);
public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool (parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
說明:創建一個帶並行級別的線程池,並行級別決定了同一時刻最多有多少個線程在執行,如不穿如並行級別參數,將默認為當前系統的CPU個數。
- ThreadPoolExecutor創建方式
多線程實現方式及區別