更多課程 選擇中心
        Java培訓

        400-111-8989

        Java培訓 > Java教程  > 正文

        Java 線程池實現的原理

        • 發布:Java培訓
        • 來源:Java教程
        • 時間:2017-09-11 10:44

        如果只講線程池的使用,那這篇文章沒有什么大的價值,充其量也就是熟悉 Executor 相關 API 的過程。

        線程池的實現過程沒有用到 Synchronized 關鍵字,用的都是 Volatile,Lock 和同步(阻塞)隊列,Atomic 相關類,FutureTask 等等,因為后者的性能更優。

        理解的過程可以很好的學習源碼中并發控制的思想。

        線程池的優點是可總結為以下三點:

        線程復用

        控制最大并發數

        管理線程

        1.線程復用過程

        理解線程復用原理首先應了解線程生命周期。

        【Java線程池實現的原理】

        在線程的生命周期中,它要經過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態。

        Thread 通過 new 來新建一個線程,這個過程是是初始化一些線程信息,如線程名,id,線程所屬 group 等,可以認為只是個普通的對象。

        調用 Thread的start() 后 Java 虛擬機會為其創建方法調用棧和程序計數器,同時將 hasBeenStarted 為 true,之后調用 start 方法就會有異常。

        處于這個狀態中的線程并沒有開始運行,只是表示該線程可以運行了。至于該線程何時開始運行,取決于 JVM 里線程調度器的調度。

        當線程獲取 cpu 后,run() 方法會被調用。不要自己去調用 Thread 的 run() 方法。

        之后根據 CPU 的調度在就緒——運行——阻塞間切換,直到 run() 方法結束或其他方式停止線程,進入 dead 狀態。

        所以實現線程復用的原理應該就是要保持線程處于存活狀態(就緒,運行或阻塞)。

        接下來來看下 ThreadPoolExecutor 是怎么實現線程復用的。

        在 ThreadPoolExecutor 主要 Worker 類來控制線程的復用。

        看下 Worker 類簡化后的代碼,這樣方便理解:

        private final class Worker implements Runnable { final Thread thread;

        Runnable firstTask;

        Worker(Runnable firstTask) { this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this);

        } public void run() {

        runWorker(this);

        } final void runWorker(Worker w) {

        Runnable task = w.firstTask;

        w.firstTask = null; while (task != null || (task = getTask()) != null){

        task.run();

        }

        }

        Worker 是一個 Runnable,同時擁有一個 thread,這個 thread 就是要開啟的線程,在新建 Worker 對象時同時新建一個 Thread 對象,同時將 Worker 自己作為參數傳入 TThread,這樣當T hread的start() 方法調用時,運行的實際上是 Worker的run() 方法,接著到 runWorker() 中,有個 while 循環,一直從 getTask() 里得到 Runnable 對象,順序執行。

        getTask() 又是怎么得到 Runnable 對象的呢?

        依舊是簡化后的代碼:

        private Runnable getTask() { if(一些特殊情況) { return null;

        }

        Runnable r = workQueue.take(); return r;

        }

        這個 workQueue 就是初始化 ThreadPoolExecutor 時存放任務的 BlockingQueue 隊列,這個隊列里的存放的都是將要執行的 Runnable 任務。

        因為 BlockingQueue 是個阻塞隊列,BlockingQueue.take() 得到如果是空,則進入等待狀態直到 BlockingQueue 有新的對象被加入時喚醒阻塞的線程。

        所以一般情況 Thread的run() 方法就不會結束,而是不斷執行從 workQueue 里的 Runnable 任務,這就達到了線程復用的原理了。

        2.控制最大并發數

        那 Runnable 是什么時候放入 workQueue?

        Worker 又是什么時候創建,Worker 里的 Thread 的又是什么時候調用 start() 開啟新線程來執行 Worker 的 run() 方法的呢?

        有上面的分析看出 Worker 里的 runWorker() 執行任務時是一個接一個,串行進行的,那并發是怎么體現的呢?

        很容易想到是在 execute(Runnable runnable) 時會做上面的一些任務。

        看下 execute 里是怎么做的。

        execute:

        簡化后的代碼

        public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); // 當前線程數 < corePoolSize

        if (workerCountOf(c) < corePoolSize) { // 直接啟動新的線程。

        if (addWorker(command, true)) return;

        c = ctl.get();

        } // 活動線程數 >= corePoolSize

        // runState為RUNNING && 隊列未滿

        if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // 再次檢驗是否為RUNNING狀態

        // 非RUNNING狀態 則從workQueue中移除任務并拒絕

        if (!isRunning(recheck) && remove(command))

        reject(command);// 采用線程池指定的策略拒絕任務

        // 兩種情況:

        // 1.非RUNNING狀態拒絕新的任務

        // 2.隊列滿了啟動新的線程失敗(workCount > maximumPoolSize)

        } else if (!addWorker(command, false)) reject(command);

        }

        addWorker:

        簡化后的代碼

        private boolean addWorker(Runnable firstTask, boolean core) { int wc = workerCountOf(c); if (wc >= (core ? corePoolSize : maximumPoolSize)) { return false;

        }

        w = new Worker(firstTask); final Thread t = w.thread;

        t.start();

        }

        根據代碼再來看上面提到的線程池工作過程中的添加任務的情況:

        * 如果正在運行的線程數量小于 corePoolSize,那么馬上創建線程運行這個任務; * 如果正在運行的線程數量大于或等于 corePoolSize,那么將這個任務放入隊列;* 如果這時候隊列滿了,而且正在運行的線程數量小于 maximumPoolSize,那么還是要創建非核心線程立刻運行這個任務;* 如果隊列滿了,而且正在運行的線程數量大于或等于 maximumPoolSize,那么線程池會拋出異常RejectExecutionException。

        這就是 Android 的 AsyncTask 在并行執行是在超出最大任務數是拋出 RejectExecutionException 的原因所在。

        通過 addWorker 如果成功創建新的線程成功,則通過 start() 開啟新線程,同時將 firstTask 作為這個 Worker 里的 run() 中執行的第一個任務。

        雖然每個 Worker 的任務是串行處理,但如果創建了多個 Worker,因為共用一個 workQueue,所以就會并行處理了。

        所以根據 corePoolSize 和 maximumPoolSize 來控制最大并發數。

        大致過程可用下圖表示。

        【Java線程池實現的原理】

        上面的講解和圖來可以很好的理解的這個過程。

        如果是做 Android 開發的,并且對 Handle r原理比較熟悉,你可能會覺得這個圖挺熟悉,其中的一些過程和 Handler,Looper,Meaasge 使用中,很相似。

        Handler.send(Message) 相當于 execute(Runnuble),Looper 中維護的 Meaasge 隊列相當于 BlockingQueue,只不過需要自己通過同步來維護這個隊列,Looper 中的 loop() 函數循環從 Meaasge 隊列取 Meaasge 和 Worker 中的 runWork() 不斷從 BlockingQueue 取 Runnable 是同樣的道理。

        3.管理線程

        通過線程池可以很好的管理線程的復用,控制并發數,以及銷毀等過程,線程的復用和控制并發上面已經講了,而線程的管理過程已經穿插在其中了,也很好理解。

        在 ThreadPoolExecutor 有個 ctl 的 AtomicInteger 變量。

        通過這一個變量保存了兩個內容:

        所有線程的數量

        每個線程所處的狀態

        其中低29位存線程數,高 3 位存 runState,通過位運算來得到不同的值。

        private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//得到線程的狀態private static int runStateOf(int c) { return c & ~CAPACITY;

        }//得到Worker的的數量private static int workerCountOf(int c) { return c & CAPACITY;

        }// 判斷線程是否在運行private static boolean isRunning(int c) { return c < SHUTDOWN;

        }

        這里主要通過 shutdown 和 shutdownNow() 來分析線程池的關閉過程。

        首先線程池有五種狀態來控制任務添加與執行。主要介紹以下三種:

        RUNNING 狀態:線程池正常運行,可以接受新的任務并處理隊列中的任務;

        SHUTDOWN 狀態:不再接受新的任務,但是會執行隊列中的任務;

        STOP 狀態:不再接受新任務,不處理隊列中的任務。

        shutdown 這個方法會將 runState 置為 SHUTDOWN,會終止所有空閑的線程,而仍在工作的線程不受影響,所以隊列中的任務人會被執行。

        shutdownNow 方法將 runState 置為 STOP。和 shutdown 方法的區別,這個方法會終止所有的線程,所以隊列中的任務也不會被執行了。

        感謝大家閱讀由java培訓機構分享的“Java 線程池實現的原理”希望對大家有所幫助,更多精彩內容請關注Java培訓官網

        免責聲明:本文由小編轉載自網絡,旨在分享提供閱讀,版權歸原作者所有,如有侵權請聯系我們進行刪除

        預約申請免費試聽課

        填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

        上一篇:10個簡單易學的Java性能優化技巧
        下一篇:JavaScript 編碼的19個小技巧??
        12大要點讓你的Java開發所向披靡~

        12大要點讓你的Java開發所向披靡~

        學習Java最好的12本免費在線電子書

        學習Java最好的12本免費在線電子書

        Java常用日志框架介紹

        Java常用日志框架介紹

        一篇文章了解RPC框架原理

        一篇文章了解RPC框架原理

        • 掃碼領取資料

          回復關鍵字:視頻資料

          免費領取 達內課程視頻學習資料

        • 視頻學習QQ群

          添加QQ群:1143617948

          免費領取達內課程視頻學習資料

        Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

        選擇城市和中心
        貴州省

        福建省

        • 達內廈門軟件園中心
        廣西省

        海南省

        国产高清情侣视频2019年,限制电影福利在线观看,23伊人大香蕉 百度 好搜 搜狗
        <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>