5、多線程創(chuàng)建的四種方式之實(shí)現(xiàn)線程池

1、為什么要使用多線程

例子:
十年前單核CPU電腦,假的多線程,像馬戲團(tuán)玩多個球,其實(shí)在在頂端每時每刻也只有一顆,CPU需要來回切換
現(xiàn)在是多核電腦,多個線程各自跑在獨(dú)立的CPU上,不用切換,效率高

2、線程池的優(yōu)勢

線程池做的工作只要是控制運(yùn)行的線程數(shù)量,處理過程中將任務(wù)放入隊(duì)列,然后在線程創(chuàng)建后啟動這些任務(wù),如果線程數(shù)量超過了最大數(shù)量,超出數(shù)量的線程則排隊(duì)等候,等待其他線程執(zhí)行完畢,釋放資源,然后再從隊(duì)列中取出任務(wù)來執(zhí)行

主要特點(diǎn):

  • 線程復(fù)用
    • 降低資源消耗,通過重復(fù)利用已經(jīng)創(chuàng)建好的線程,從而降低線程創(chuàng)建和銷毀造成的消耗
  • 控制最大并發(fā)數(shù)
    • 提高響應(yīng)速度,當(dāng)任務(wù)到達(dá)時,任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行
  • 管理線程
    • 提高線程的可管理性,線程是稀缺資源,如果無限制的創(chuàng)建,不僅僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,起到調(diào)優(yōu)和監(jiān)控的功能

3、Executor框架

Java中的線程池是通過Executor框架實(shí)現(xiàn)的,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個類


4、線程池七大參數(shù)

(1)corePoolSize:線程池中常駐核心線程數(shù)

(2)maximumPoolSize:線程池能夠容納同時執(zhí)行的最大線程數(shù),此值必須大于等于1

(3)keepAliveTime:多余的空閑線程存活時間。當(dāng)前線程池?cái)?shù)量超過corePoolSize時,當(dāng)空閑時間到達(dá)keepAliveTime值時,多余空閑線程會被銷毀直到只剩下corePoolSize個線程為止。

(4)unit:keepAliveTime的時間單位

(5)workQueue:任務(wù)隊(duì)列,被提交但尚未執(zhí)行的任務(wù)

(6)threadFactory:表示生成線程池中的工作線程的線程工廠,用于創(chuàng)建線程,一般為默認(rèn)線程工廠即可

(7)handler:拒絕策略,表示當(dāng)隊(duì)列滿了并且工作線程大于等于線程池的最大線程數(shù)(maximumPoolSize)時如何來拒絕來請求的Runnable的策略

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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
 

5、線程池的使用

5.1、Executors.newFixedThreadPool(int)

  • 執(zhí)行長期任務(wù)性能好,創(chuàng)建一個線程池,
    一池有N個固定的線程,有固定線程數(shù)的線程
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

  • newFixedThreadPool創(chuàng)建的線程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue

5.2、Executors.newSingleThreadExecutor()

  • 一個任務(wù)一個任務(wù)的執(zhí)行,一池一線程
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • newSingleThreadExecutor 創(chuàng)建的線程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue

5.3、Executors.newCachedThreadPool()

  • 執(zhí)行很多短期異步任務(wù),線程池根據(jù)需要創(chuàng)建新線程,
    但在先前構(gòu)建的線程可用時將重用它們??蓴U(kuò)容,遇強(qiáng)則強(qiáng)
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • newCachedThreadPool創(chuàng)建的線程池將corePoolSize設(shè)置為0,將maximumPoolSize設(shè)置為Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是說來了任務(wù)就創(chuàng)建線程運(yùn)行,當(dāng)線程空閑超過60秒,就銷毀線程。
/**
 * 線程池
 * Arrays
 * Collections
 * Executors
 */
public class MyThreadPoolDemo {

    public static void main(String[] args) {
        //固定數(shù)的線程池,一池五線程

//       ExecutorService threadPool =  Executors.newFixedThreadPool(5); //一個銀行網(wǎng)點(diǎn),5個受理業(yè)務(wù)的窗口
//       ExecutorService threadPool =  Executors.newSingleThreadExecutor(); //一個銀行網(wǎng)點(diǎn),1個受理業(yè)務(wù)的窗口
       ExecutorService threadPool =  Executors.newCachedThreadPool(); //一個銀行網(wǎng)點(diǎn),可擴(kuò)展受理業(yè)務(wù)的窗口

        //10個顧客請求
        try {
            for (int i = 1; i <=10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t 辦理業(yè)務(wù)");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

6、線程池底層原理


流程
1、在創(chuàng)建了線程池后,開始等待請求。

2、當(dāng)調(diào)用execute()方法添加一個請求任務(wù)時,線程池會做出如下判斷:
2.1如果正在運(yùn)行的線程數(shù)量小于corePoolSize,那么馬上創(chuàng)建線程運(yùn)行這個任務(wù);
2.2如果正在運(yùn)行的線程數(shù)量大于或等于corePoolSize,那么將這個任務(wù)放入隊(duì)列;
2.3如果這個時候隊(duì)列滿了且正在運(yùn)行的線程數(shù)量還小于maximumPoolSize,那么還是要創(chuàng)建非核心線程立刻運(yùn)行這個任務(wù);
2.4如果隊(duì)列滿了且正在運(yùn)行的線程數(shù)量大于或等于maximumPoolSize,那么線程池會啟動飽和拒絕策略來執(zhí)行。

3、當(dāng)一個線程完成任務(wù)時,它會從隊(duì)列中取下一個任務(wù)來執(zhí)行。

4、當(dāng)一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
如果當(dāng)前運(yùn)行的線程數(shù)大于corePoolSize,那么這個線程就被停掉。
所以線程池的所有任務(wù)完成后,它最終會收縮到corePoolSize的大小。

7、線程池的拒絕策略

拒絕策略

  • 等待隊(duì)列已經(jīng)排滿了,再也塞不下新任務(wù)了
  • 同時,線程池中的max線程也達(dá)到了,無法繼續(xù)為 新任務(wù)服務(wù)
  • 這個時候就需要拒絕策略機(jī)制合理的處理這個問題

JDK內(nèi)置的拒絕策略
以下策略均實(shí)現(xiàn)RejectedExecutionHandle接口

  • AbortPolicy(默認(rèn)):直接拋出RejectedExecutionException異常阻止系統(tǒng)正常運(yùn)行
  • CallerRunsPolicy:“調(diào)用者運(yùn)行”一種調(diào)節(jié)機(jī)制,該策略既不會拋棄任務(wù),也不
    會拋出異常,而是將某些任務(wù)回退到調(diào)用者,從而降低新任務(wù)的流量。
  • DiscardOldestPolicy:拋棄隊(duì)列中等待最久的任務(wù),然后把當(dāng)前任務(wù)加人隊(duì)列中
    嘗試再次提交當(dāng)前任務(wù)。
  • DiscardPolicy:該策略默默地丟棄無法處理的任務(wù),不予任何處理也不拋出異常。
    如果允許任務(wù)丟失,這是最好的一種策略。

8、日常開發(fā)中并不會去使用系統(tǒng)提供的創(chuàng)建線程池的方法

9、自定義線程池

public class MyThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(),
                //new ThreadPoolExecutor.AbortPolicy()
                //new ThreadPoolExecutor.CallerRunsPolicy()
                //new ThreadPoolExecutor.DiscardOldestPolicy()
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
        //10個顧客請求
        try {
            for (int i = 1; i <= 10; i++) {
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 辦理業(yè)務(wù)");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容