簡述ThreadPoolExecutor的工作原理

ThreadPoolExecutor的內部工作原理,整體思路總結爲5句話:

  1. 如果線程池大小poolSize小於corePoolSize,則創建新線程執行任務。
  2. 如果線程池大小poolSize大於corePoolSize,且等待隊列未滿,則進入等待隊列。
  3. 如果線程池大小poolSize大於corePoolSize且小於maximumPoolSize,且等待隊列已滿,則創建新線程執行任務。
  4. 如果線程池大小poolSize大於corePoolSize且大於maximumPoolSize,且等待隊列已滿,則調用拒絕策略來處理該任務。
  5. 線程池裏的每個線程執行完任務後不會立刻退出,而是會去檢查下等待隊列裏是否還有線程任務需要執行,如果在keepAliveTime裏等不到新的任務了,那麼線程就會退出。

ThreadPoolExecutor線程池中拒絕策略:

  1. AbortPolicy:爲java線程池默認的阻塞策略,不執行此任務,而且會直接拋出一個執行時異常,切記TreadPoolExecutor.execute需要try catch,否則程序會直接退出。
  2. DiscardPolicy:直接拋棄,任務不執行,空方法
  3. DiscardOldestPolicy:從隊列裏面拋棄head的一個任務,並再次execute 此任務(task)
  4. CallerRunsPolicy:在調用execute的線程裏面執行此command,會阻塞入口。
  5. 用戶自定義拒絕策略:實現RejectdExecutionHandler,並自己定義策略模式。

舉例說明:

使用直接丟棄任務本身的拒絕策略:DiscardPolicy

import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;

    public class ExecutorDemo {

        private static  SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        public static void main(String[] args) {
            int corePoolSize = 1;
            int maximumPoolSize = 1;
            BlockingQueue queue = new  ArrayBlockingQueue<Runnable>(1);
            ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize,  maximumPoolSize, 
                    0, TimeUnit.SECONDS, queue ) ;
            pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy ());
            for(int i=0;i<10;i++){
                final int index = i;
                pool.submit(new Runnable(){

                    @Override
                    public void run() {
                        log(Thread.currentThread().getName()+"begin run task :"+index);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        log(Thread.currentThread().getName()+" finish run  task :"+index);
                    }

                });
            }

            log("main thread before sleep!!!");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log("before shutdown()");

            pool.shutdown();

            log("after shutdown(),pool.isTerminated=" + pool.isTerminated());
            try {
                pool.awaitTermination(1000L, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log("now,pool.isTerminated=" + pool.isTerminated());
        }

        protected static void log(String string) {
            System.out.println(sdf.format(new Date())+"  "+string);
        }

    }

運行結果:

    2016-08-04 22:29:21  main thread before sleep!!!
    2016-08-04 22:29:21  pool-1-thread-1begin run task :0
    2016-08-04 22:29:22  pool-1-thread-1 finish run  task :0
    2016-08-04 22:29:22  pool-1-thread-1begin run task :1
    2016-08-04 22:29:23  pool-1-thread-1 finish run  task :1
    2016-08-04 22:29:25  before shutdown()
    2016-08-04 22:29:25  after shutdown(),pool.isTerminated=false
    2016-08-04 22:29:25  now,pool.isTerminated=true

從結果可以看出,只有task0和task1兩個任務被執行了。

爲什麼只有task0和task1兩個任務被執行了呢?

過程是這樣的:由於我們的任務隊列的容量爲1.當task0正在執行的時候,task1被提交到了隊列中但是還沒有執行,受隊列容量的限制,submit提交的task2~task9就都被直接拋棄了。因此就只有task0和task1被執行了。

使用丟棄任務隊列中比較久的任務的拒絕策略:DiscardOldestPolicy

如果將拒絕策略改爲:DiscardOldestPolicy(丟棄隊列中比較久的任務)

運行結果爲:

    2016-08-04 22:31:58  pool-1-thread-1begin run task :0
    2016-08-04 22:31:58  main thread before sleep!!!
    2016-08-04 22:31:59  pool-1-thread-1 finish run  task :0
    2016-08-04 22:31:59  pool-1-thread-1begin run task :9
    2016-08-04 22:32:00  pool-1-thread-1 finish run  task :9
    2016-08-04 22:32:02  before shutdown()
    2016-08-04 22:32:02  after shutdown(),pool.isTerminated=false
    2016-08-04 22:32:02  now,pool.isTerminated=true

從結果可以看出,只有task0和task9被執行了。

使用將任務將由調用者線程去執行的拒絕策略:CallerRunsPolicy

如果將拒絕策略改爲:CallerRunsPolicy(即不用線程池中的線程執行,而是交給調用方來執行)

運行結果爲:

    2016-08-04 22:33:07  mainbegin run task :2
    2016-08-04 22:33:07  pool-1-thread-1begin run task :0
    2016-08-04 22:33:08  main finish run  task :2
    2016-08-04 22:33:08  mainbegin run task :3
    2016-08-04 22:33:08  pool-1-thread-1 finish run  task :0
    2016-08-04 22:33:08  pool-1-thread-1begin run task :1
    2016-08-04 22:33:09  pool-1-thread-1 finish run  task :1
    2016-08-04 22:33:09  main finish run  task :3
    2016-08-04 22:33:09  mainbegin run task :5
    2016-08-04 22:33:09  pool-1-thread-1begin run task :4
    2016-08-04 22:33:10  main finish run  task :5
    2016-08-04 22:33:10  mainbegin run task :7
    2016-08-04 22:33:10  pool-1-thread-1 finish run  task :4
    2016-08-04 22:33:10  pool-1-thread-1begin run task :6
    2016-08-04 22:33:11  main finish run  task :7
    2016-08-04 22:33:11  mainbegin run task :9
    2016-08-04 22:33:11  pool-1-thread-1 finish run  task :6
    2016-08-04 22:33:11  pool-1-thread-1begin run task :8
    2016-08-04 22:33:12  main finish run  task :9
    2016-08-04 22:33:12  main thread before sleep!!!
    2016-08-04 22:33:12  pool-1-thread-1 finish run  task :8
    2016-08-04 22:33:16  before shutdown()
    2016-08-04 22:33:16  after shutdown(),pool.isTerminated=false
    2016-08-04 22:33:16  now,pool.isTerminated=true

從結果可以看出,沒有任務被拋棄,而是將由的任務分配到main線程中執行了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章