懶漢式
import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolService { private static final int DEFAULT_CORE_SIZE=100; private static final int MAX_QUEUE_SIZE=500; private volatile static ThreadPoolExecutor executor; private ThreadPoolService() {}; // 獲取單例的線程池對象 public static ThreadPoolExecutor getInstance() { if (executor == null) { synchronized (ThreadPoolService.class) { if (executor == null) { executor = new ThreadPoolExecutor(DEFAULT_CORE_SIZE,// 核心線程數 MAX_QUEUE_SIZE, // 最大線程數 Integer.MAX_VALUE, // 閑置線程存活時間 TimeUnit.MILLISECONDS,// 時間單位 new LinkedBlockingDeque<Runnable>(Integer.MAX_VALUE),// 線程隊列 Executors.defaultThreadFactory()// 線程工廠 ); } } } return executor; } public void execute(Runnable runnable) { if (runnable == null) { return; } executor.execute(runnable); } // 從線程隊列中移除對象 public void cancel(Runnable runnable) { if (executor != null) { executor.getQueue().remove(runnable); } } }
靜態參數(餓漢式)
import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.*; /** * 異步任務處理器 */ public class AsyncTaskExecutor { /** 線程池保持ALIVE狀態線程數 */ public static final int CORE_POOL_SIZE = 10; /** 線程池最大線程數 */ public static final int MAX_POOL_SIZE = 40; /** 空閑線程回收時間 */ public static final int KEEP_ALIVE_TIME = 1000; /** 線程池等待隊列 */ public static final int BLOCKING_QUEUE_SIZE = 1000; /** 業務請求異步處理線程池 */ private static final ThreadPoolExecutor processExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<Runnable>(BLOCKING_QUEUE_SIZE), new TreadFactoryBuilder.setNameFormat("boomoom-thread-pool-%d").build(), new TreadPoolExecutor.DiscardPolicy()); private AsyncTaskExecutor() {}; /** * 異步任務處理 * * @param task 任務 */ public void execute(Runnable task) { processExecutor.submit(task); } }
在項目中,以上兩種方式都使用過,主要看線程任務在項目里的位置。采用第二種的,項目的主要業務就是異步線程來實現。
比較:餓漢式是線程安全的,在類創建的同時就已經創建好一個靜態的對象供系統使用,以后不再改變。
懶漢式如果在創建實例對象時不加上synchronized則會導致對對象的訪問不是線程安全的,推薦使用第一種。
從實現方式來講他們最大的區別就是懶漢式是延時加載,
懶漢式是在需要的時候才創建對象,而餓漢式在虛擬機啟動的時候就會創建。
餓漢式無需關注多線程問題、寫法簡單明了、能用則用。但是它是加載類時創建實例、所以如果是一個工廠模式、緩存了很多實例、那么就得考慮效率問題,因為這個類一加載則把所有實例不管用不用一塊創建。
懶漢式的優點是延時加載、缺點是應該用同步(比如double-check)、其實也可以不用同步、看你的需求了,多創建一兩個無引用的廢對象其實也沒什么大不了。
實踐:
@Test public void threadPool() { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); Date startDate = new Date(); System.out.println("開始時間:"+sf.format(startDate)); for(int i=0;i<300000;i++){ System.out.println("i=" + i); //啟動線程 ThreadPoolService.getInstance().execute(() -> { int total = 0; for(int k=0;k<1000;k++){ total = total + k; } System.out.println("total=" + total); }); System.out.println("結束了"); Date endDate = new Date(); System.out.println("結束時間:"+sf.format(endDate)); System.out.println("耗時,單位秒:"+ (endDate.getTime()-startDate.getTime())/1000); }
}
運行結果:
程序可以正常關閉