池技術是性能優化的重要手段:鏈接池,線程池已是開發中的標配了。面試中這個知識點也是高頻問題。抽空學習了Java的ThreadPoolExecutor, 把學習的思路記錄一下。java
因爲線程的建立和銷燬都是系統層面的操做,涉及到系統資源的佔用和回收,因此建立線程是一個重量級的操做。爲了提高性能,就引入了線程池;即線程複用。Java不只提供了線程池,還提供了線程池的操做工具類。 咱們由淺入深瞭解一下。面試
import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadDemo { static class Worker implements Runnable{ public void run(){ System.out.println("run work "+Thread.currentThread().getName() ); } } public static void main(String[] args) { Worker w1 = new Worker(); ExecutorService service = Executors.newFixedThreadPool(10); service.submit(w1); service.shutdown(); } }
看Executors的源碼,發現其使用的是ThreadPoolExecutor。 研究一下ThreadPoolExecutor, 發現其默認的參數Executors.defaultThreadFactory(), defaultHandler
。線程池的建立工廠默認以下:性能優化
public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; }
也就是自定義了一下線程的名字,將線程歸到了同一個組。
線程池的defaultHandler
以下:ide
public static class AbortPolicy implements RejectedExecutionHandler { /** * Creates an {@code AbortPolicy}. */ public AbortPolicy() { } /** * Always throws RejectedExecutionException. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task * @throws RejectedExecutionException always. */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
也就是說,當提交的任務超過線程池的容量,那麼就會拋出RejectedExecutionException
異常。 可是使用Executors會發現,並無拋出異常。這是由於Executors建立BlockingQueue
時沒有指定隊列的容量。工具
換言之,線程池能容納的任務數量最多爲maximumPoolSize
+ queueSize
。 好比線程池以下new ThreadPoolExecutor(10, 11, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(5));
則最大任務數量爲16個,超過16個就會拋出異常。性能
線程池中線程數量有多少呢?先運行以下的代碼:學習
import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; public class ThreadDemo { static class Worker implements Runnable{ public void run(){ try { Thread.sleep(1000); System.out.println("done work "+Thread.currentThread().getName() ); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Worker w1 = new Worker(); ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(5)); for(int i=0;i<9;i++) { executor.submit(w1); } executor.shutdown(); } }
能夠發現最多開啓了4個線程。 這4個線程就對應了4個Worker的實例。
看worker的源碼能夠發現,它兼備AQS和Runnable兩個特性。 咱們只關注它Runnable的特性。優化
while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } }
在這個線程中不斷從隊列中獲取任務,而後執行。 worker中反覆出現的ctl
又是什麼呢?ui
ctl是兩個變量組合,一個32位的int, 高3位用於控制線程池的狀態,低29位用於記錄線程池啓動線程的數量。
因此有這麼幾個方法this
// Packing and unpacking ctl private static int runStateOf(int c) { return c & ~CAPACITY; } private static int workerCountOf(int c) { return c & CAPACITY; } private static int ctlOf(int rs, int wc) { return rs | wc; }
整個線程池的核心,就worker
和ctl
的理解。 有點複雜,主要是集中了:
1. AQS 2. BlockingQueue
這也是爲何我建議先學AQS,後學線程池的實現原理。