【高併發】通過源碼深度解析ThreadPoolExecutor類是如何保證線程池正確運行的

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"大家好,我是冰河~~","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於線程池的核心類ThreadPoolExecutor來說,有哪些重要的屬性和內部類爲線程池的正確運行提供重要的保障呢?","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"ThreadPoolExecutor類中的重要屬性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在ThreadPoolExecutor類中,存在幾個非常重要的屬性和方法,接下來,我們就介紹下這些重要的屬性和方法。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"ctl相關的屬性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AtomicInteger類型的常量ctl是貫穿線程池整個生命週期的重要屬性,它是一個原子類對象,主要用來保存線程的數量和線程池的狀態,我們看下與這個屬性相關的代碼如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//主要用來保存線程數量和線程池的狀態,高3位保存線程狀態,低29位保存線程數量\nprivate final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));\n//線程池中線程的數量的位數(32-3)\nprivate static final int COUNT_BITS = Integer.SIZE - 3;\n//表示線程池中的最大線程數量\n//將數字1的二進制值向右移29位,再減去1\nprivate static final int CAPACITY = (1 << COUNT_BITS) - 1;\n//線程池的運行狀態\nprivate static final int RUNNING = -1 << COUNT_BITS;\nprivate static final int SHUTDOWN = 0 << COUNT_BITS;\nprivate static final int STOP = 1 << COUNT_BITS;\nprivate static final int TIDYING = 2 << COUNT_BITS;\nprivate static final int TERMINATED = 3 << COUNT_BITS;\n//獲取線程狀態\nprivate static int runStateOf(int c) { return c & ~CAPACITY; }\n//獲取線程數量\nprivate static int workerCountOf(int c) { return c & CAPACITY; }\nprivate static int ctlOf(int rs, int wc) { return rs | wc; }\nprivate static boolean runStateLessThan(int c, int s) {\n return c < s;\n}\nprivate static boolean runStateAtLeast(int c, int s) {\n return c >= s;\n}\nprivate static boolean isRunning(int c) {\n return c < SHUTDOWN;\n}\nprivate boolean compareAndIncrementWorkerCount(int expect) {\n return ctl.compareAndSet(expect, expect + 1);\n}\nprivate boolean compareAndDecrementWorkerCount(int expect) {\n return ctl.compareAndSet(expect, expect - 1);\n}\nprivate void decrementWorkerCount() {\n do {} while (! compareAndDecrementWorkerCount(ctl.get()));\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於線程池的各狀態說明如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RUNNING:運行狀態,能接收新提交的任務,並且也能處理阻塞隊列中的任務","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SHUTDOWN: 關閉狀態,不能再接收新提交的任務,但是可以處理阻塞隊列中已經保存的任務,當線程池處於RUNNING狀態時,調用shutdown()方法會使線程池進入該狀態","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"STOP: 不能接收新任務,也不能處理阻塞隊列中已經保存的任務,會中斷正在處理任務的線程,如果線程池處於RUNNING或SHUTDOWN狀態,調用shutdownNow()方法,會使線程池進入該狀態","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TIDYING: 如果所有的任務都已經終止,有效線程數爲0(阻塞隊列爲空,線程池中的工作線程數量爲0),線程池就會進入該狀態。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TERMINATED: 處於TIDYING狀態的線程池調用terminated ()方法,會使用線程池進入該狀態","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也可以按照ThreadPoolExecutor類的註釋,將線程池的各狀態之間的轉化總結成如下圖所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/90/90ce6bfb358e096a6efedc3c6a0be12b.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RUNNING -> SHUTDOWN:顯式調用shutdown()方法, 或者隱式調用了finalize()方法","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(RUNNING or SHUTDOWN) -> STOP:顯式調用shutdownNow()方法","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SHUTDOWN -> TIDYING:當線程池和任務隊列都爲空的時候","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"STOP -> TIDYING:當線程池爲空的時候","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TIDYING -> TERMINATED:當 terminated() hook 方法執行完成時候","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"其他重要屬性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了ctl相關的屬性外,ThreadPoolExecutor類中其他一些重要的屬性如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//用於存放任務的阻塞隊列 \nprivate final BlockingQueue workQueue;\n//可重入鎖\nprivate final ReentrantLock mainLock = new ReentrantLock();\n//存放線程池中線程的集合,訪問這個集合時,必須獲得mainLock鎖\nprivate final HashSet workers = new HashSet();\n//在鎖內部阻塞等待條件完成\nprivate final Condition termination = mainLock.newCondition();\n//線程工廠,以此來創建新線程\nprivate volatile ThreadFactory threadFactory;\n//拒絕策略\nprivate volatile RejectedExecutionHandler handler;\n//默認的拒絕策略\nprivate static final RejectedExecutionHandler defaultHandler = new AbortPolicy();\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"ThreadPoolExecutor類中的重要內部類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在ThreadPoolExecutor類中存在對於線程池的執行至關重要的內部類,Worker內部類和拒絕策略內部類。接下來,我們分別看這些內部類。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Worker內部類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Worker類從源代碼上來看,實現了Runnable接口,說明其本質上是一個用來執行任務的線程,接下來,我們看下Worker類的源代碼,如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private final class Worker extends AbstractQueuedSynchronizer implements Runnable{\n private static final long serialVersionUID = 6138294804551838833L;\n //真正執行任務的線程\n final Thread thread;\n //第一個Runnable任務,如果在創建線程時指定了需要執行的第一個任務\n //則第一個任務會存放在此變量中,此變量也可以爲null\n //如果爲null,則線程啓動後,通過getTask方法到BlockingQueue隊列中獲取任務\n Runnable firstTask;\n //用於存放此線程完全的任務數,注意:使用了volatile關鍵字\n volatile long completedTasks;\n \n //Worker類唯一的構造放大,傳遞的firstTask可以爲null\n Worker(Runnable firstTask) {\n //防止在調用runWorker之前被中斷\n setState(-1);\n this.firstTask = firstTask;\n //使用ThreadFactory 來創建一個新的執行任務的線程\n this.thread = getThreadFactory().newThread(this);\n }\n //調用外部ThreadPoolExecutor類的runWorker方法執行任務\n public void run() {\n runWorker(this);\n }\n\n //是否獲取到鎖 \n //state=0表示鎖未被獲取\n //state=1表示鎖被獲取\n protected boolean isHeldExclusively() {\n return getState() != 0;\n }\n\n protected boolean tryAcquire(int unused) {\n if (compareAndSetState(0, 1)) {\n setExclusiveOwnerThread(Thread.currentThread());\n return true;\n }\n return false;\n }\n\n protected boolean tryRelease(int unused) {\n setExclusiveOwnerThread(null);\n setState(0);\n return true;\n }\n\n public void lock() { acquire(1); }\n public boolean tryLock() { return tryAcquire(1); }\n public void unlock() { release(1); }\n public boolean isLocked() { return isHeldExclusively(); }\n\n void interruptIfStarted() {\n Thread t;\n if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {\n try {\n t.interrupt();\n } catch (SecurityException ignore) {\n }\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Worker類的構造方法中,可以看出,首先將同步狀態state設置爲-1,設置爲-1是爲了防止runWorker方法運行之前被中斷。這是因爲如果其他線程調用線程池的shutdownNow()方法時,如果Worker類中的state狀態的值大於0,則會中斷線程,如果state狀態的值爲-1,則不會中斷線程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Worker類實現了Runnable接口,需要重寫run方法,而Worker的run方法本質上調用的是ThreadPoolExecutor類的runWorker方法,在runWorker方法中,會首先調用unlock方法,該方法會將state置爲0,所以這個時候調用shutDownNow方法就會中斷當前線程,而這個時候已經進入了runWork方法,就不會在還沒有執行runWorker方法的時候就中斷線程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意:大家需要重點理解","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}},{"type":"strong","attrs":{}},{"type":"strong","attrs":{}}],"text":"Worker","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"類的實現。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"拒絕策略內部類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在線程池中,如果workQueue阻塞隊列滿了,並且沒有空閒的線程池,此時,繼續提交任務,需要採取一種策略來處理這個任務。而線程池總共提供了四種策略,如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"直接拋出異常,這也是默認的策略。實現類爲AbortPolicy。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用調用者所在的線程來執行任務。實現類爲CallerRunsPolicy。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"丟棄隊列中最靠前的任務並執行當前任務。實現類爲DiscardOldestPolicy。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"直接丟棄當前任務。實現類爲DiscardPolicy。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在ThreadPoolExecutor類中提供了4個內部類來默認實現對應的策略,如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static class CallerRunsPolicy implements RejectedExecutionHandler {\n\n public CallerRunsPolicy() { }\n\n public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {\n if (!e.isShutdown()) {\n r.run();\n }\n }\n}\n\npublic static class AbortPolicy implements RejectedExecutionHandler {\n\n public AbortPolicy() { }\n\n public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {\n throw new RejectedExecutionException(\"Task \" + r.toString() + \" rejected from \" + e.toString());\n }\n}\n\npublic static class DiscardPolicy implements RejectedExecutionHandler {\n\n public DiscardPolicy() { }\n\n public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {\n }\n}\n\npublic static class DiscardOldestPolicy implements RejectedExecutionHandler {\n\n public DiscardOldestPolicy() { }\n\n\n public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {\n if (!e.isShutdown()) {\n e.getQueue().poll();\n e.execute(r);\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們也可以通過實現RejectedExecutionHandler接口,並重寫RejectedExecutionHandler接口的rejectedExecution方法來自定義拒絕策略,在創建線程池時,調用ThreadPoolExecutor的構造方法,傳入我們自己寫的拒絕策略。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,自定義的拒絕策略如下所示。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CustomPolicy implements RejectedExecutionHandler {\n\n public CustomPolicy() { }\n\n public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {\n if (!e.isShutdown()) {\n System.out.println(\"使用調用者所在的線程來執行任務\")\n r.run();\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用自定義拒絕策略創建線程池。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"new ThreadPoolExecutor(0, Integer.MAX_VALUE,\n 60L, TimeUnit.SECONDS,\n new SynchronousQueue(),\n Executors.defaultThreadFactory(),\n new CustomPolicy());\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"好了,今天就到這兒吧,我是冰河,我們下期見~~","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章