ExecutorService線程池+七大參數源碼分析+自定義線程池

例子:
10年前單核CPU電腦,假的多線程,像馬戲團小丑玩多個球,CPU需要來回切換。
現在是多核電腦,多個線程各自跑在獨立的CPU上,不用切換效率高。


線程池的優勢:


線程池做的工作只要是控制運行的線程數量,處理過程中將任務放入隊列,然後在線程創建後啓動這些任務,如果線程數量超過了最大數量,超出數量的線程排隊等候,等其他線程執行完畢,再從隊列中取出任務來執行。
 
它的主要特點爲:線程複用;控制最大併發數;管理線程。
 
第一:降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的銷耗。
第二:提高響應速度。當任務到達時,任務可以不需要等待線程創建就能立即執行。
第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。

線程池常用的三個實現類

package main.test;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: wdq
 * @Date: 2020/4/21 11:21
 * @Description:
 */
public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor1 = Executors.newFixedThreadPool(5);//一池5線程
        ExecutorService executor2 = Executors.newSingleThreadExecutor();//一池單線程
        ExecutorService executor3 = Executors.newCachedThreadPool();//一池N線程

//        try {
//            for (int i = 0; i < 10; i++) {
//                executor1.execute(()->{
//                    System.out.println(Thread.currentThread().getName()+"辦理業務");
//                });
//                try { TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
//            }
//        }catch (Exception e){
//            e.printStackTrace();
//        }finally {
//            executor1.shutdown();
//        }

//        try {
//            for (int i = 0; i < 10; i++) {
//                executor2.execute(()->{
//                    System.out.println(Thread.currentThread().getName()+"辦理業務");
//                });
//            }
//        }catch (Exception e){
//            e.printStackTrace();
//        }finally {
//            executor2.shutdown();
//        }

        try {
            for (int i = 0; i < 10; i++) {
                executor3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"辦理業務");
                });
                //休眠1毫秒
                //try { TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            executor3.shutdown();
        }

    }

}

三個實現類的底層分析

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

上面是三個實現類的源碼,可以看出,它們的底層實際上都是ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
ThreadPoolExecutor的七大參數:
corePoolSize:線程池中的常駐核心線程數;
maximumPoolSize:線程池中能夠容納同時執行的最大線程數,此值必須大於等於1;
keepAliveTime:多餘的空閒線程的存活時間,當前池中線程數量超過corePoolSize時,當空閒時間達到KeepAliveTime時,多餘線程會被銷燬直到剩下corPoolSize個線程爲止;
TimeUnit unit:keepAliveTime的單位;
BlockingQueue<Runnable> workQueue:任務隊列,被提交但尚未被執行的任務;
ThreadFactory threadFactory:表示生成線程池中工作線程的線程工廠,用於創建線程,一般默認的即可;
RejectedExecutionHandler handler:拒絕策略,表示當隊列滿了,並且工作線程大於等於線程池的最大線程數時,如何來拒絕請求執行的runnable的策略。

具體算法:

1、在創建了線程池後,開始等待請求。
2、當調用execute()方法添加一個請求任務時,線程池會做出如下判斷:
  2.1如果正在運行的線程數量小於corePoolSize,那麼馬上創建線程運行這個任務;
  2.2如果正在運行的線程數量大於或等於corePoolSize,那麼將這個任務放入隊列;
  2.3如果這個時候隊列滿了且正在運行的線程數量還小於maximumPoolSize,那麼還是要創建非核心線程立刻運行這個任務;
  2.4如果隊列滿了且正在運行的線程數量大於或等於maximumPoolSize,那麼線程池會啓動飽和拒絕策略來執行。

3、當一個線程完成任務時,它會從隊列中取下一個任務來執行。
4、當一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
    如果當前運行的線程數大於corePoolSize,那麼這個線程就被停掉。
    所以線程池的所有任務完成後,它最終會收縮到corePoolSize的大小。

自定義線程池

所以不允許使用Executes那三個系統提供的線程池,需要去自定義線程池。

package main.test;

import jdk.nashorn.internal.runtime.regexp.JoniRegExp;

import java.util.concurrent.*;

/**
 * @Auther: wdq
 * @Date: 2020/4/21 15:11
 * @Description:
 */
public class DivThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(
                3,
                7,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        try {
            for (int i = 0; i < 11; i++) {
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"辦理業務");
                });
                //休眠1毫秒
                //try { TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            executorService.shutdown();
        }
    }
}

上面定義的線程池,最大可接受10個請求(maximumPoolSize+LinkedBlockingQueue),如果超過10個請求,就會觸發拒絕機制。因爲自定義的觸發機制使用的是AbortPolicy(默認的),它會拋出異常RejectedExecutionException。

四種拒絕機制:

AbortPolicy:之間拋出RejectedExecutionException異常組織系統正常運行。

CallerRunsPolicy:調用者運行一種調節機制,該策略既不會拋棄任務,也不會拋出異常,而是回退給任務發出者。

DiscardOldestPolicy:拋棄隊列中等待最久的任務,然後把當前任務加到隊列中,嘗試再次提交當前任務。

DiscardPolicy:該策略默默丟棄無法處理的任務,不予任何處理也不拋異常。如果任務運行丟棄,這是最好的一種策略。

以上拒絕策略均實現了RejectedExecutionHandler。

如果加上休眠,看結果:

package main.test;

import jdk.nashorn.internal.runtime.regexp.JoniRegExp;

import java.util.concurrent.*;

/**
 * @Auther: wdq
 * @Date: 2020/4/21 15:11
 * @Description:
 */
public class DivThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(
                3,
                7,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        try {
            for (int i = 0; i < 14; i++) {
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"辦理業務");
                });
                //休眠1毫秒
                try { TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            executorService.shutdown();
        }
    }
}
/*
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
*/

 

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