日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

1 池化技術(shù)之線程池

什么是池化技術(shù)?簡(jiǎn)單來(lái)說(shuō)就是優(yōu)化資源的使用,我準(zhǔn)備好了一些資源,有人要用就到我這里拿,用完了就還給我。而一個(gè)比較重要的的實(shí)現(xiàn)就是線程池。那么線程池用到了池化技術(shù)有什么好處呢?

  • 降低資源的消耗
  • 提高響應(yīng)的速度
  • 方便管理

也就是 線程復(fù)用、可以控制最大并發(fā)數(shù)、管理線程

2 線程池的五種實(shí)現(xiàn)方式

其實(shí)線程池我更愿意說(shuō)成四種封裝實(shí)現(xiàn)方式,一種原始實(shí)現(xiàn)方式。這四種封裝的實(shí)現(xiàn)方式都是依賴(lài)于最原始的的實(shí)現(xiàn)方式。所以這里我們先介紹四種封裝的實(shí)現(xiàn)方式

2.1 newSingleThreadExecutor()

這個(gè)線程池很有意思,說(shuō)是線程池,但是池子里面只有一條線程。如果線程因?yàn)楫惓6V梗瑫?huì)自動(dòng)新建一個(gè)線程補(bǔ)充。我們可以測(cè)試一下:我們對(duì)線程池執(zhí)行十條打印任務(wù),可以發(fā)現(xiàn)它們用的都是同一條線程

    public static void test01() {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        try {
            //對(duì)線程進(jìn)行執(zhí)行十條打印任務(wù)
            for(int i = 1; i <= 10; i++){
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"=>執(zhí)行完畢!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //用完線程池一定要記得關(guān)閉
            threadPool.shutdown();
        }
    }
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!

2.2 newFixedThreadPool(指定線程數(shù)量)

這個(gè)線程池是可以指定我們的線程池大小的,可以針對(duì)我們具體的業(yè)務(wù)和情況來(lái)分配大小。它是創(chuàng)建一個(gè)核心線程數(shù)跟最大線程數(shù)相同的線程池,因此池中的線程數(shù)量既不會(huì)增加也不會(huì)變少,如果有空閑線程任務(wù)就會(huì)被執(zhí)行,如果沒(méi)有就放入任務(wù)隊(duì)列,等待空閑線程。我們同樣來(lái)測(cè)試一下:

    public static void test02() {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        try {
            //對(duì)線程進(jìn)行執(zhí)行十條打印任務(wù)
            for(int i = 1; i <= 10; i++){
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"=>執(zhí)行完畢!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //用完線程池一定要記得關(guān)閉
            threadPool.shutdown();
        }
    }

我們創(chuàng)建了五條線程的線程池,在打印任務(wù)的時(shí)候,可以發(fā)現(xiàn)線程都有進(jìn)行工作

pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!
pool-1-thread-3=>執(zhí)行完畢!
pool-1-thread-5=>執(zhí)行完畢!
pool-1-thread-4=>執(zhí)行完畢!

2.3 newCachedThreadPool()

這個(gè)線程池是創(chuàng)建一個(gè)核心線程數(shù)為0,最大線程為Inter.MAX_VALUE的線程池,也就是說(shuō)沒(méi)有限制,線程池中的線程數(shù)量不確定,但如果有空閑線程可以復(fù)用,則優(yōu)先使用,如果沒(méi)有空閑線程,則創(chuàng)建新線程處理任務(wù),處理完放入線程池。我們同樣來(lái)測(cè)試一下

關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

2.4 newScheduledThreadPool(指定最大線程數(shù)量)

創(chuàng)建一個(gè)沒(méi)有最大線程數(shù)限制的可以定時(shí)執(zhí)行線程池在這里,還有創(chuàng)建一個(gè)只有單個(gè)線程的可以定時(shí)執(zhí)行線程池(Executors.newSingleThreadScheduledExecutor())這些都是上面的線程池?cái)U(kuò)展開(kāi)來(lái)了,不詳細(xì)介紹了。

3 介紹線程池的七大參數(shù)

上面我們也說(shuō)到了線程池有五種實(shí)現(xiàn)方式,但是實(shí)際上我們就介紹了四種。那么最后一種是什么呢?不急,我們可以點(diǎn)開(kāi)我們上面線程池實(shí)現(xiàn)方式的源碼進(jìn)行查看,可以發(fā)現(xiàn)

  • newSingleThreadExecutor()的實(shí)現(xiàn)源碼
關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

而點(diǎn)開(kāi)其他幾個(gè)線程池到最后都可以發(fā)現(xiàn),他們實(shí)際上用的就是這個(gè)ThreadPoolExecutor。我們把源代碼粘過(guò)來(lái)分析,其實(shí)也就是這七大參數(shù)

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    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;
    }

毫無(wú)懸念,這就是最后一種方式,也是其他實(shí)現(xiàn)方式的基礎(chǔ)。而用這種方式也是最容易控制,因?yàn)槲覀兛梢宰杂傻脑O(shè)置參數(shù)。在阿里巴巴開(kāi)發(fā)手冊(cè)中也提到了

關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

所以我們更需要去了解這七大參數(shù),在平時(shí)用線程池的時(shí)候盡量去用ThreadPoolExecutor。而關(guān)于這七大參數(shù)我們簡(jiǎn)單概括就是

  • corePoolSize: 線程池核心線程個(gè)數(shù)
  • workQueue: 用于保存等待執(zhí)行任務(wù)的阻塞隊(duì)列
  • maximunPoolSize: 線程池最大線程數(shù)量
  • ThreadFactory: 創(chuàng)建線程的工廠
  • RejectedExecutionHandler: 隊(duì)列滿(mǎn),并且線程達(dá)到最大線程數(shù)量的時(shí)候,對(duì)新任務(wù)的處理策略
  • keeyAliveTime: 空閑線程存活時(shí)間
  • TimeUnit: 存活時(shí)間單位

3.1 而關(guān)于線程池最大線程數(shù)量,我們也有兩種設(shè)置方式

  1. CPU密集型
    獲得cpu的核數(shù),不同的硬件不一樣,設(shè)置核數(shù)的的線程數(shù)量。
    我們可以通過(guò)代碼Runtime.getRuntime().availableProcessors();獲取,然后設(shè)置。
  2. IO密集型
    IO非常消耗資源,所以我們需要計(jì)算大型的IO程序任務(wù)有多少個(gè)。
    一般來(lái)說(shuō),線程池最大值 > 大型任務(wù)的數(shù)量即可
    一般設(shè)置大型任務(wù)的數(shù)量*2

這里我們用一個(gè)例子可以更好理解這些參數(shù)在線程池里面的位置和作用。如圖1.0,我們這是一個(gè)銀行

關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

我們一共有五個(gè)柜臺(tái),可以理解為線程池的最大線程數(shù)量,而其中有兩個(gè)是在營(yíng)業(yè)中,可以理解為線程池核心線程個(gè)數(shù)。而下面的等待廳可以理解為用于保存等待執(zhí)行任務(wù)的阻塞隊(duì)列。銀行就是創(chuàng)建線程的工廠。而關(guān)于空閑線程存活時(shí)間,我們可以理解為如圖1.1這種情況,當(dāng)五個(gè)營(yíng)業(yè)中,卻只有兩個(gè)人需要被服務(wù),而其他三個(gè)人一直處于等待的情況下,等了一個(gè)小時(shí)了,他們被通知下班了。這一個(gè)小時(shí)時(shí)間就可以說(shuō)是空閑線程存活時(shí)間,而存活時(shí)間單位,顧名思義。

關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

到現(xiàn)在我們就剩一個(gè)拒絕策略還沒(méi)介紹,什么是拒絕策略呢?我們可以假設(shè)當(dāng)銀行五個(gè)柜臺(tái)都有人在被服務(wù),如圖1.2。而等待廳這個(gè)時(shí)候也是充滿(mǎn)了人,銀行實(shí)在容不下人了。

關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

這個(gè)時(shí)候?qū)︺y行外面那個(gè)等待的人的處理策略就是拒絕策略。我們同樣了解之后用代碼來(lái)測(cè)試一下:

    public static  void test05(){
        ExecutorService threadPool = new ThreadPoolExecutor(
                //核心線程數(shù)量
                2,
                //最大線程數(shù)量
                5,
                //空閑線程存活時(shí)間
                3,
                //存活單位
                TimeUnit.SECONDS,
                //這里我們使用大多數(shù)線程池都默認(rèn)使用的阻塞隊(duì)列,并使容量為3
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                //我們使用默認(rèn)的線程池都默認(rèn)用的拒絕策略
                new ThreadPoolExecutor.AbortPolicy()

        );
        try {
            //對(duì)線程進(jìn)行執(zhí)行十條打印任務(wù)
            for(int i = 1; i <= 2; i++){
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"=>執(zhí)行完畢!");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //用完線程池一定要記得關(guān)閉
            threadPool.shutdown();
        }

    }

我們執(zhí)行打印兩條任務(wù),可以發(fā)現(xiàn)線程池只用到了我們的核心兩條線程,相當(dāng)于只有兩個(gè)人需要被服務(wù),所以我們就開(kāi)了兩個(gè)柜臺(tái)。

pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!

但是在我們將打印任務(wù)改到大于5的時(shí)候,(我們改成8)我們可以發(fā)現(xiàn)線程池的五條線程都在使用了,人太多了,我們的銀行需要都開(kāi)放了來(lái)服務(wù)。

for(int i = 1; i <= 8; i++)
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!
pool-1-thread-3=>執(zhí)行完畢!
pool-1-thread-4=>執(zhí)行完畢!
pool-1-thread-5=>執(zhí)行完畢!

在我們改成大于8的時(shí)候,可以發(fā)現(xiàn)拒絕策略觸發(fā)了。銀行實(shí)在容納不下了,所以我們把外面那個(gè)人用策略打發(fā)了。

for(int i = 1; i <= 9; i++)
關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

在這里我們也可以得出一個(gè)結(jié)論:線程池大小= 最大線程數(shù) + 阻塞隊(duì)列大小

在上面我們?cè)谑褂玫淖枞?duì)列是大多數(shù)的線程池都使用的阻塞隊(duì)列,所以就引發(fā)思考下面這個(gè)問(wèn)題。

3.2 為什么大部分的線程池都用LinkedBlockingQueue?

  • LinkedBlockingQueue 使用單向鏈表實(shí)現(xiàn),在聲明LinkedBlockingQueue的時(shí)候,可以不指定隊(duì)列長(zhǎng)度,長(zhǎng)度為Integer.MAX_VALUE, 并且新建了一個(gè)Node對(duì)象,Node對(duì)象具有item,next變量,item用于存儲(chǔ)元素,next指向鏈表下一個(gè)Node對(duì)象,在剛開(kāi)始的時(shí)候鏈表的head,last都指向該Node對(duì)象,item、next都為null,新元素放在鏈表的尾部,并從頭部取元素。取元素的時(shí)候只是一些指針的變化,LinkedBlockingQueue給put(放入元素),take(取元素)都聲明了一把鎖,放入和取互不影響,效率更高。
  • ArrayBlockingQueue 使用數(shù)組實(shí)現(xiàn),在聲明的時(shí)候必須指定長(zhǎng)度,如果長(zhǎng)度太大,造成內(nèi)存浪費(fèi),長(zhǎng)度太小,并發(fā)性能不高,如果數(shù)組滿(mǎn)了,就無(wú)法放入元素,除非有其他線程取出元素,放入和取出都使用同一把鎖,因此存在競(jìng)爭(zhēng),效率比LinkedBlockingQueue低。

4 四種策略

我們?cè)谑褂肨hreadPoolExecutor的時(shí)候是可以自己選擇拒絕策略的,而拒絕策略我們所知道的有四種。

  • AbortPolicy(被拒絕了拋出異常)
  • CallerRunsPolicy(使用調(diào)用者所在線程執(zhí)行,就是哪里來(lái)的回哪里去)
  • DiscardOldestPolicy(嘗試去競(jìng)爭(zhēng)第一個(gè),失敗了也不拋異常)
  • DiscardPolicy(默默丟棄、不拋異常)

4.1 AbortPolicy

我們?cè)谏厦媸褂玫木褪茿bortPolicy拒絕策略,在執(zhí)行打印任務(wù)超出線程池大小的時(shí)候,拋出了異常。

關(guān)于線程池的五種實(shí)現(xiàn)方式,七大參數(shù),四種拒絕策略

 

4.2 CallerRunsPolicy

我們將拒絕策略修改為CallerRunsPolicy,執(zhí)行后可以發(fā)現(xiàn),因?yàn)榈诰艂€(gè)打印任務(wù)被拒絕了,所以它被調(diào)用者所在的線程執(zhí)行了,也就是我們的main線程。(因?yàn)樗鼜膍ain線程來(lái)的,現(xiàn)在又回到了main線程。所以我們說(shuō)它從哪里來(lái)回哪里去)

        ExecutorService threadPool = new ThreadPoolExecutor(
                //核心線程數(shù)量
                2,
                //最大線程數(shù)量
                5,
                //空閑線程存活時(shí)間
                3,
                //存活單位
                TimeUnit.SECONDS,
                //這里我們使用大多數(shù)線程池都默認(rèn)使用的阻塞隊(duì)列,并使容量為3
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                //我們使用默認(rèn)的線程池都默認(rèn)用的拒絕策略
                new ThreadPoolExecutor.CallerRunsPolicy()

        );
pool-1-thread-2=>執(zhí)行完畢!
main=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-3=>執(zhí)行完畢!
pool-1-thread-4=>執(zhí)行完畢!
pool-1-thread-5=>執(zhí)行完畢!

4.3 DiscardOldestPolicy

嘗試去競(jìng)爭(zhēng)第一個(gè)任務(wù),但是失敗了。這里就沒(méi)顯示了,也不拋出異常。

pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!
pool-1-thread-3=>執(zhí)行完畢!
pool-1-thread-4=>執(zhí)行完畢!
pool-1-thread-5=>執(zhí)行完畢!

4.4 DiscardPolicy

多出來(lái)的任務(wù),默默拋棄掉,也不拋出異常。

pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-1=>執(zhí)行完畢!
pool-1-thread-2=>執(zhí)行完畢!
pool-1-thread-3=>執(zhí)行完畢!
pool-1-thread-4=>執(zhí)行完畢!
pool-1-thread-5=>執(zhí)行完畢!

可以看到我們的DiscardOldestPolicy與DiscardPolicy一樣的結(jié)果,但是它們其實(shí)是不一樣,正如我們最開(kāi)始總結(jié)的那樣,DiscardOldestPolicy在多出的打印任務(wù)的時(shí)候會(huì)嘗試去競(jìng)爭(zhēng),而不是直接拋棄掉,但是很顯然競(jìng)爭(zhēng)失敗不然也不會(huì)和DiscardPolicy一樣的執(zhí)行結(jié)果。但是如果在線程比較多的時(shí)候就可以很看出來(lái)。

分享到:
標(biāo)簽:線程
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定