使用線程池優化多線程編程
認識線程池
在Java中,所有的對象都是需要通過new操作符來創建的,如果創建大量短生命周期的對象,將會使得整個程序的性能非常的低下。這種時候就需要用到了池的技術,比如數據庫連接池,線程池等。
在java1.5之后,java自帶了線程池,在util包下新增了concurrent包,這個包主要作用就是介紹java線程和線程池如何使用的。
在包java.util.concurrent下的 Executors類中定義了Executor、ExecutorService、ScheduledExecutorService、ThreadFactoryScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
此類支持以下各種方法:
a.創建並返回設置有常用配置字符串的 ExecutorService 的方法。
b.創建並返回設置有常用配置字符串的 ScheduledExecutorService 的方法。
c.創建並返回“包裝的”ExecutorService 方法,它通過使特定於實現的方法不可訪問來禁用重新配置。
d.創建並返回 ThreadFactory 的方法,它可將新創建的線程設置為已知的狀態。
e.創建並返回非閉包形式的 Callable 的方法,這樣可將其用於需要 Callable 的執行方法中。
首先我們先來比較一下用線程池創建多個線程和用獨立運行的方式創建多個線程的區別,這里我們將通過比較兩種方法占有內存和花費時間,來說明線程池的必要性和重要性。
代碼實例:
package com.xhj.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 比較獨立創建和線程池創建線程的優劣 比較因素--時間和占用內存 * * @author XIEHEJUN * */ public class CompareThreadPool implements Runnable { private int id = 0; @Override public void run() { id++; } public static void main(String[] args) { /** * 獨立創建1000個線程 */ { // 獲取當前程序運行時對象 Runtime run = Runtime.getRuntime(); // 調用垃圾回收機制,以減少內存誤差 run.gc(); // 獲取當前JVM的空閑內存 long freeMemory = run.freeMemory(); // 系統當前時間 long timePro = System.currentTimeMillis(); // 獨立創建並執行1000個線程 for (int i = 0; i < 1000; i++) { new Thread(new CompareThreadPool()).start(); } System.out.println("獨立創建並執行1000個線程所需要占用的內存大小: " + (freeMemory - run.freeMemory())); System.out.println("獨立創建並運行1000個線程需要的時間為: " + (System.currentTimeMillis() - timePro)); } /** * 利用線程池創建1000個線程 */ { // 獲取當前程序運行時對象 Runtime run = Runtime.getRuntime(); // 調用垃圾回收機制,以減少內存誤差 run.gc(); // 獲取當前JVM的空閑內存 long freeMemory = run.freeMemory(); // 系統當前時間 long timePro = System.currentTimeMillis(); ExecutorService service = Executors.newFixedThreadPool(2); // 線程池創建並執行1000個線程 for (int i = 0; i < 1000; i++) { service.submit(new CompareThreadPool()); } System.out.println("使用線程池創建1000個線程所需要占用的內存大小: " + (freeMemory - run.freeMemory())); // 線程池使用完成,關閉線程池 service.shutdown(); System.out.println("使用線程池創建並運行1000個線程需要的時間為: " + (System.currentTimeMillis() - timePro)); } } }
結果為:
結論--為什么要用線程池:
通過上面這個例子,我們知道使用線程池可以大大的提高系統的性能,提高程序任務的執行效率,節約了系統的內存空間。在線程池中,每一個工作線程都可以被重復利用,可執行多個任務,減少了創建和銷毀線程的次數。能夠根據系統的承受能力,調整其線程數目,以便使系統達到運行的最佳效果。
線程池原理:
一個線程池中有多個處於可運行狀態的線程,當向線程池中添加Runnable或Callable接口對象時,就會有一個線程來執行run()方法或call()方法。如果方法執行完畢,則該線程並不終止,而是繼續在池中處於可運行狀態,以運行新的任務。
了解線程池(java中創建線程池的幾種常用靜態方法)
在java中,線程池的頂級接口是util.concurrent包下的Executors工具類,在這個類里,定義了很多操作線程池的方法。
其中最常用的線程池有:
1.創建單線程的線程池:
newSingleThreadExecutor(),創建一個只有一個線程的線程池,此單線程按照任務的提交順序執行所有的任務,
若遇到異常中斷,線程池則會重新建立一個單線程來替代其完成后續工作。
代碼實例:
/** * 創建一個單線程的線程池 * * @return */ public ExecutorService SingleThreadPool() { ExecutorService singlePool = Executors.newSingleThreadExecutor(); return singlePool; }
2.創建一個可緩存的線程池:
newCachedThreadPool(),創建一個不限制大小,且只能的線程池,他會根據任務量的多少來開辟和減少內存空間,但是線程池中線程的大小依賴於系統的性能或者JVM的容量
代碼實例:
/** * 創建一個可緩存線程池 * * @return */ public ExecutorService CachedThreadPool() { ExecutorService cachedPool = Executors.newCachedThreadPool(); return cachedPool; }
3.創建一個大小固定的線程池:
newFixedThreadPool(),創建一個固定大小的線程池,任務提交則建立線程,直到線程大小達到線程池允許最大值,若某個線程結束,線程池則補充一個新的線程。
代碼實例:
/** * 創建一個大小固定的線程池 * * @return */ public ExecutorService FixedThreadPool() { ExecutorService fixedPool = Executors.newFixedThreadPool(2); return fixedPool; }
4.創建一個可定時、周期性執行的線程池
newScheduledThreadPool(),創建一個可定時、周期性執行的線程池,此線程池沒有大小限制,實現周期性任務調度。
代碼實例:
ScheduledThreadPoolExecutor scheduledPool = new ScheduledThreadPoolExecutor(1);
為了便於大家理解和對比其不同之處,下面將把這幾個常用線程池整合到一個程序當中,
代碼實例:
package com.xhj.thread; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 幾個常用線程池的理解與運用 * * @author XIEHEJUN * */ public class CreateThreadPools extends Thread { @Override public void run() { System.out.println("系統時間 : " + System.currentTimeMillis() + " 線程: " + Thread.currentThread().getName() + "正在執行!!"); } /** * 創建一個單線程的線程池 * * @return */ public ExecutorService SingleThreadPool() { ExecutorService singlePool = Executors.newSingleThreadExecutor(); return singlePool; } /** * 創建一個大小固定的線程池 * * @return */ public ExecutorService FixedThreadPool() { ExecutorService fixedPool = Executors.newFixedThreadPool(3); return fixedPool; } /** * 創建一個可緩存線程池 * * @return */ public ExecutorService CachedThreadPool() { ExecutorService cachedPool = Executors.newCachedThreadPool(); return cachedPool; } /** * 將創建好的線程放入線程池,並執行 * * @param pool */ public void service(ExecutorService pool) { // 創建線程 Thread thread1 = new CreateThreadPools(); Thread thread2 = new CreateThreadPools(); Thread thread3 = new CreateThreadPools(); Thread thread4 = new CreateThreadPools(); Thread thread5 = new CreateThreadPools(); // 線程入線程池,並執行 pool.execute(thread1); pool.execute(thread2); pool.execute(thread3); pool.execute(thread4); pool.execute(thread5); // 關閉線程池 pool.shutdown(); } /** * 創建一個大小無限制的線程池,可用與定時和周期性服務 */ public void scheduledThreadPool() { ScheduledThreadPoolExecutor scheduledPool = new ScheduledThreadPoolExecutor(1); scheduledPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("=======" + System.currentTimeMillis() + "========="); } }, 1000, 5000, TimeUnit.MILLISECONDS); scheduledPool.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(System.nanoTime()); } }, 1000, 2000, TimeUnit.MILLISECONDS); } public static void main(String[] args) { CreateThreadPools creatThreadPool = new CreateThreadPools(); Scanner sc = new Scanner(System.in); while (true) { System.out.println("請選擇創建線程池:1.單線程線程池;2.可緩存線程池;3.固定大小線程池;4可定時周期性執行線程池"); int i = sc.nextInt(); switch (i) { case 1: System.out.println("-----調用單線程的線程池-----"); // 調用單線程的線程池 creatThreadPool.service(creatThreadPool.SingleThreadPool()); break; case 2: System.out.println("-----調用可緩存線程的線程池-----"); // 調用可緩存線程的線程池 creatThreadPool.service(creatThreadPool.CachedThreadPool()); break; case 3: System.out.println("-----調用固定大小線程的線程池-----"); // 調用固定大小線程的線程池 creatThreadPool.service(creatThreadPool.FixedThreadPool()); break; case 4: System.out.println("-----調用大小無限制可定時和周期性執行的線程池-----"); // 調用固定大小線程的線程池 creatThreadPool.scheduledThreadPool(); break; } } } }
注:當線程池任務結束之后,一定要記得將線程池關閉,執行shutdown()方法。