隨筆篇-線程池

線程池

1. 簡介

當一個程序中需要多個task需要被并發(fā)執(zhí)行時糯俗,最直接的方式是為每一個task創(chuàng)建一個線程去執(zhí)行尽纽,但這樣會帶來以下問題:

  • 大量線程創(chuàng)建與運行會導致系統(tǒng)不斷的啟動和關閉新線程裆馒,會過渡消耗系統(tǒng)資源.

  • 過度切換線程的危險楼誓,從而可能導致系統(tǒng)的崩潰.

  • 同時創(chuàng)建過多的線程意味著要創(chuàng)建過多的Thread對象俘闯,這樣也會額外增大jvm的垃圾回收壓力

在這種情況下,引入"池化技術"是必要的肃拜,在實際開發(fā)中這種技術得到很多的應用痴腌,例如數據庫連接池等

池化技術可以簡單理解為是一個池子,在這個池子存放著固定的資源燃领,這些資源可以是線程士聪,也可以數據庫連接,具體取決于是怎么類型的池

這些資源不會"消失"猛蔽,而是可以被多次復用剥悟,從而達到節(jié)省資源開銷等目的

image-20211102172615693

線程池就是"池化技術"的一種體現,使用該技術可以避免上文中提到的問題曼库,并且好處眾多区岗,如下:

  • 不需要創(chuàng)建大量線程,只需要創(chuàng)建一個線程池即可毁枯,讓線程池去管理線程
  • 加快程序響應速度慈缔,合理利用CPU資源
  • ......

2. 參數

創(chuàng)建線程池會需要傳入幾個參數才能創(chuàng)建成功,如下:

參數名 說明
corePoolSize 核心線程數
maxPoolSize 最大線程數
keepAliveTime 保持存活時間
workQueue 任務存儲隊列
threadFactory 線程工廠
rejectHandler 拒絕處理器

關于線程池中線程創(chuàng)建過程大體流程如下(含參數解釋):

  • 當線程池創(chuàng)建并初始化完成种玛,此時線程池里面并沒有任何資源藐鹤,當有任務過來需要被執(zhí)行時才會去創(chuàng)建核心線程執(zhí)行任務

  • 線程不會無限制創(chuàng)建,當創(chuàng)建線程數超過corePoolSize時赂韵,就會把任務存儲在workQueue

    如果創(chuàng)建的線程數沒有超過corePoolSize時娱节,即使線程池有線程時空閑的,也還是會創(chuàng)建線程

  • workQueue中存儲任務已滿祭示,則再會去創(chuàng)建線程從隊列中拉取任務執(zhí)行肄满,線程也不會無限制創(chuàng)建,當創(chuàng)建的線程達到maxPoolSize時,則不會創(chuàng)建了

  • 當線程達到maxPoolSize,且workQueue存滿時稠歉,則會根據rejectHandler執(zhí)行拒絕策略

  • 最后多余非核心線程的空閑時間超過配置的keepAliveTime讥电,那么線程進行停止銷毀

    核心線程會一直存在,不會銷毀

具體流程圖如下:

image-20211102180822666

ThreadFactory

線程工廠轧抗,用來創(chuàng)建線程的恩敌,在創(chuàng)建線程池時,可以使用開發(fā)人員定義的線程工廠横媚,也可以使用默認提供的ThreadFactory

默認的ThreadFactory使用Executors.defaultThreadFactory()創(chuàng)建纠炮,該線程工程創(chuàng)建出來的線程都是在同一個線程組,且都不是守護線程

如果開發(fā)人員想要自己指定創(chuàng)建出來的線程名灯蝴,線程組恢口,優(yōu)先級,是否為守護線程則可以使用自己的線程工廠


WorkQueue

工作隊列穷躁,或者叫任務隊列耕肩,用來存儲任務,當核心線程已滿问潭,且核心線程都忙碌時猿诸,則將任務存儲到工作隊列中,直接工作隊列也存儲滿狡忙,才會去創(chuàng)建非核心線程梳虽,如上文圖

工作隊列為阻塞隊列,常用一般分為三個,可以由開發(fā)人員自由選擇

  • SynchronousQueue

    該隊列實現了BlockingQueue,類圖如下:

    image-20211103102056762

    該隊列可以理解為是一個直接交接隊列灾茁,或者為中轉隊列窜觉,當有任務過來,該隊列就會立馬交給線程池的線程執(zhí)行

    該隊列是沒有容量的北专,因此如果線程池采用這種可以將此案成maxPoolSize設置大一些

    這樣只要有任務過來禀挫,該隊列就會交給線程池執(zhí)行

  • LinkedBlockingQueue

    該隊列可以理解為"無邊界隊列",同樣的也實現了BlockingQueue,如下:

    image-20211103103420548

    當創(chuàng)建該隊列時拓颓,如果沒有指定大小语婴,那么該隊列則無上限

    如果采用該隊列,意味著maxPoolSize參數無作用录粱,因為隊列有可能永遠存儲不滿

    注意:其實也不是存儲不滿腻格,存儲的上限時Integer.MAX_VALUE

  • ArrayBlockingQueue

    該隊列可以理解為"有邊界隊列",同樣的也實現了BlockingQueue,如下:

    image-20211103103549737

    這種隊列創(chuàng)建就需要指定大小啥繁,這樣也就意味著maxPoolSize是有意義的了

    具體選擇何種隊列,則需要根據具體場景來進行選擇

3. 創(chuàng)建

線程池創(chuàng)建分為兩大類:

  • 手動創(chuàng)建

    手動創(chuàng)建青抛,即自己創(chuàng)建線程池對象ThreadPoolExecutor,這種方式可以更加的去理解線程池規(guī)則,規(guī)避風險

    在阿里巴巴開發(fā)手冊中,也提到過赦肃,系統(tǒng)開發(fā)過程中,不允許使用jdk默認提供的線程池嫡意,如圖:

    image-20211103104631291
  • 自動創(chuàng)建

    自動創(chuàng)建即使用jdk默認提供的線程池,雖然阿里巴巴開發(fā)手冊上不允許使用捣辆,但是還是需要了解一下

3.1 自動

jdk默認封裝的線程池主要有以下:

  • FixedThreadPool
  • SingleThreadPool
  • CachedThreadPool
  • ScheduledThreadPool

接下來就從其說明蔬螟,使用,缺點問題方面探討這些線程池之間的優(yōu)缺點


3.1.1 FixedThreadPool

  • 說明

    這種線程池創(chuàng)建時汽畴,只需要傳入一個核心線程數即可旧巾,如下:

     ExecutorService executor = Executors.newFixedThreadPool(2);
    

    從源碼其源碼可知,該線程池核心線程數與最大線程數一樣忍些,采用LinkedBlockingQueue,如下:

    image-20211103112142577

    從源碼可知鲁猩,其最大線程數并沒有任何意義,其工作隊列無上限罢坝,意味著任務的存儲無上限(其實也不是無上限廓握,最大上限為Integer.maxValue)

  • 使用

    使用該類型線程池處理線程,如下:

    package com.tomato.thread.pool;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class FixThreadPool {
        public static void main(String[] args) {
            // 創(chuàng)建線程池嘁酿,并指定核心線程數為2
            ExecutorService executorService = Executors.newFixedThreadPool(2);
    
            for (int i = 0; i < 10; i++) {
                // 循環(huán)往線程池中提交任務,循環(huán)十次隙券,即意味著提交了十個任務
                // 由于核心線程只有2個,意味著最多只有2個線程在執(zhí)行任務
                // 沒有執(zhí)行的任務就在等待
               // 任務都是Runable類型對象闹司,所以這里為了簡便用了lamda
                executorService.execute(() -> {
                    System.out.println(
                    Thread.currentThread().getName());
                });
    
            }
        }
    
    }
    

    運行結果如下:

    image-20211103113109106

    從圖中可以看出是尔,最多兩個線程在執(zhí)行任務

  • 缺點

    由于任務存儲隊列沒有上限,假如執(zhí)行的任務耗時較久开仰,在任務較多的情況下拟枚,就意味著存儲隊列中會不停的存儲任務,這樣會導致最后oom,如圖:

    image-20211109102829059

    代碼演示如下:

    package com.tomato.thread.pool;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class FixThreadPoolOOM {
        public static void main(String[] args) {
            // 為了更好的測試众弓,這里將核心線程數設置為1
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            for(int i = 1; i < 100_000; i++) {
                // 往線程池中添加10W個任務
                executorService.execute(() -> {
                    try {
                        // 這里休眠50s是為了模擬耗時操作
                        // 當核心在執(zhí)行該任務時恩溅,其他任務沒有線程去被執(zhí)行就只能先存儲在隊列中
                        // 如果隊列中元素過多肯定會報oom
                        Thread.sleep(50_000);
                        System.out.println(
                            Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
    
    

    為了更加明顯突出,直接修改-Xmx 6m -Xms 6m谓娃,調小jvm運行內存脚乡,如下圖:

    image-20211103115319924

    結果如下:

    image-20211103121610336

3.1.2 SingleThreadPool

該線程池從名字可以看出是單個線程,因此在創(chuàng)建線程池的時候也不需要傳入核心線程數滨达,如下:

 ExecutorService executor = Executors.newSingleThreadExecutor();

從其創(chuàng)建的源碼可知奶稠,該線程池使用的也是"無邊界阻塞隊列",如下:

image-20211109094219578

因此如果在當個任務處理的情況下,也會發(fā)生OOM捡遍,原理同FixThreadPool原理一樣锌订,這里就不再演示

3.1.3 CachedThreadPool

  • 說明

    該線程池與之前線程池創(chuàng)建一樣,不需要傳入核心線程數画株,如下:

     ExecutorService executor = Executors.newCachedThreadPool();
    

    但是通過源碼可以得知辆飘,該線程池的核心線程為0啦辐,而非核心線程數為Integer.MAX_VALUE,使用的也是中轉隊列蜈项,如下:

    image-20211109101404414
  • 使用

    使用該線程池處理任務代碼如下:

    package com.tomato.thread.pool;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import static java.text.MessageFormat.format;
    
    public class CacheThreadPoool {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newCachedThreadPool();
    
            for(int i = 0; i < 1_000; i++) {
                executorService.execute(() -> {
                    System.out.println(format("{0}執(zhí)行任務",Thread.currentThread().getName()));
                });
            }
        }
    }
    

    運行結果如下:

    image-20211109101959313
  • 缺點

    從其源碼可知芹关,當有任務過來會立馬交給線程池的線程執(zhí)行,而其核心線程為0紧卒,最大線程為Integer.MAX_VALUE

    如果當執(zhí)行的任務是一個耗時任務侥衬,在任務較多的情況下,就會頻繁的創(chuàng)建線程對象跑芳,從而有可能發(fā)生OOM,如下:

    image-20211109103029903

    代碼演示如下:

    package com.tomato.thread.pool;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import static java.text.MessageFormat.format;
    
    
    public class CacheThreadPoolOOM {
        public static void main(String[] args) {
            ExecutorService executor = Executors.newCachedThreadPool();
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                executor.execute(() -> {
                    try {
                        Thread.sleep(50_000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(format("{0}執(zhí)行任務完成", Thread.currentThread().getName()));
                });
            }
        }
    }
    
    

    同時為了更快看到效果轴总,這里設置JVM參數-Xms2m -Xmx2m,如下:

    image-20211109103845255

    因此在阿里開發(fā)規(guī)范中,該類型的線程池與上述類型的兩種線程池是不允許使用的

3.1.4 ScheduledThreadPool

該類型的線程池聋亡,是一個具備周期性執(zhí)行的一個線程池肘习,在這里不再解釋,之前的章節(jié)有過詳細描述

3.2 手動

  • 說明

    在上述中主要是利用jdk提供的線程池去進行創(chuàng)建坡倔,但是也提到了每個線程池的局限性漂佩,因此在實際開發(fā)中手動創(chuàng)建線程池機會反而多點

  • 代碼

    因此在具體創(chuàng)建時,代碼如下:

    package com.tomato.thread.pool;
    
    import java.text.MessageFormat;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    import static java.text.MessageFormat.*;
    
    /**
     * 自定義線程池
     */
    public class CustomThreadPool {
        public static void main(String[] args) {
            ThreadPoolExecutor executor =
                    new ThreadPoolExecutor(
                            2,
                            10, 
                            0,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<>(20));
            for(int i = 0; i < 30; i++) {
                executor.execute(() -> {
                    System.out.println(format("{0}執(zhí)行任務",Thread.currentThread().getName()));
                });
            }
        }
    }
    
    

    執(zhí)行結果如下:

    image-20211109112033050

    這樣做的好處可以自己控制核心線程數罪塔,最大線程數投蝉,以及存儲隊列等

4. 核心數

當手動創(chuàng)建線程池時,如何去確定核心線程數為多少征堪,目前在一些實踐中主要分為以下兩類:

  • CPU密集型(經常加密瘩缆,計算hash等)

    線程核心數此時應該為 CPU可用核數的1~2倍

  • IO密集型(數據庫讀寫,文件讀寫佃蚜,網絡讀寫)

    一般該線程數為核心線程數的很多倍庸娱,根據 Brain Goetz的公式為:

    CPU 核心數 * (1 + 平均等待時間/平均工作時間)

5. Factory

在上述中,不管是手動創(chuàng)建線程池還是自動創(chuàng)建線程池谐算,使用的ThreadFactory都是默認的線程工廠

有時候在創(chuàng)建線程池時熟尉,想要修改線程池一些參數,例如線程名字等洲脂,這樣就可以使用自定義線程工廠斤儿,如下:

package com.tomato.thread.pool;

import java.text.MessageFormat;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static java.text.MessageFormat.*;

public class CustomThreadFactory implements ThreadFactory {
    private ThreadGroup threadGroup;

    private AtomicLong threadNumber = new AtomicLong(1L);

    /**
     * 線程池中線程組名字前綴
     */
    private String prefix;

    public CustomThreadFactory() {
        this("");
    }

    public CustomThreadFactory(String prefixName) {
        SecurityManager securityManager = System.getSecurityManager();
        threadGroup = securityManager != null ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();
        prefix = (prefixName != null && !"".equals(prefixName)) ? prefixName : "tomato-pool-thread";
    }

    @Override
    public Thread newThread(Runnable r) {
        /**
         * 當設置stackSize屬于<=0 時,以-Xss為準
         * 當設置stackSize屬于(0, 4k]區(qū)間時恐锦,設置的-Xss會失效往果,棧空間取默認值1M
         * 當設置stackSize屬于(4k, 64k]區(qū)間時一铅,設置的-Xss會失效陕贮,棧空間取4k馅闽。
         * 當設置stackSize屬于(64k, 128k]區(qū)間時飘蚯,設置的-Xss會失效馍迄,椄R玻空間取64k局骤。
         * 當設置stackSize屬于 >128k 時,設置的-Xss會失效暴凑,椔退Γ空間取stackSize本身
         */
        Thread thread = new Thread(threadGroup,r, prefix + "-" + threadNumber.getAndIncrement(), 0);

        if (thread.isDaemon()) {
            thread.setDaemon(false);
        }

        if (thread.getPriority() != Thread.NORM_PRIORITY) {
            thread.setPriority(Thread.NORM_PRIORITY);
        }

        return thread;
    }
}

class Test {
    public static final Integer ACPU = Runtime.getRuntime().availableProcessors();
    public static void main(String[] args) {
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(
                        ACPU * 2 + 1,
                        ACPU * 2 + 1,
                        0,
                        TimeUnit.SECONDS,
                        new LinkedBlockingDeque<>(600),
                        new CustomThreadFactory());

        for(int i = 1; i < 1_00; i++) {
            executor.execute(() -> {
                System.out.println(format("{0}執(zhí)行任務完成",Thread.currentThread().getName()));
            });
        }
    }
}

執(zhí)行結果,發(fā)現線程的名字都改變现喳,如下:

image-20211109134421034

這樣就完成了自定義ThreadFactory的實現

6. Reject

從上文的描述中可知凯傲,線程池的流程是當任務存儲隊列滿了的時候,則會創(chuàng)建非核心線程去執(zhí)行隊列中的任務

那如果所有的線程都處于忙碌中嗦篱,且隊列中存儲滿了冰单,那么當任務再過來就會執(zhí)行配置的

拒絕策略去拒絕任務,在線程池中拒絕策略主要有三個灸促,如下:

如果線程池異常關閉诫欠,有任務過來也會異常拒絕

  • AbortPolicy

    終止策略,當任務被拒絕時浴栽,則拋出RejectExecutionException異常

    這種策略也是默認的策略

  • CallerRunsPolicy

    調用者策略荒叼,如果線程池使用該策略拒絕了該任務,那么該任務由哪個線程提交的就由哪個線程執(zhí)行

  • DiscardOldestPolicy

    丟棄最早未處理請求策略典鸡,當使用該策略時被廓,線程池會丟棄最先進入阻塞隊列中的任務,給最新的任務騰出空間

  • DiscardPolicy

    丟棄策略萝玷,使用該策略嫁乘,就會丟棄最新任務

  • 自定義

    當然也可以根據其策略去自定義拒絕策略

關于四種策略,其詳細解釋如下文

6.1 AbortPolicy

  • 說明

    該策略為終止策略球碉,也是線程默認的策略蜓斧,其原理就是當隊列存儲已滿,且無任何空閑線程時汁尺,就會拋出RejectExcutionException,原理圖如下:

    image-20211109141419872

    同時從源碼可以看出該策略法精,其方式就是拋出異常,如下:

    image-20211109141752480
  • 使用

    創(chuàng)建線程池使用該策略痴突,如下:

    package com.tomato.thread.pool;
    
    import java.text.MessageFormat;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 策略方式為拋異常
     */
    public class AbortPolicyTest {
        public static void main(String[] args) {
            final Integer ACPU = Runtime.getRuntime().availableProcessors();
            ThreadPoolExecutor executor =
                    new ThreadPoolExecutor(
                            2 * ACPU + 1,
                            2 * ACPU + 1,
                            0,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<>(6),
                            new ThreadPoolExecutor.AbortPolicy());
            for (int i = 0; i <= 100; i++) {
                executor.execute(() -> {
                    try {
                        // 模擬耗時
                        Thread.sleep(100);
                        System.out.println(MessageFormat.format("{0}執(zhí)行完成", Thread.currentThread().getName()));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
    
    

    從代碼可以看出搂蜓,阻塞隊列最大容量是6,而添加的任務是100辽装,因此當超過容量帮碰,且線程都處于忙碌時,則會拋出異常拾积,如下:

    image-20211109143945153

6.2 CallerRunsPolicy

  • 說明

    如果創(chuàng)建線程池使用該策略拒絕了該任務殉挽,那么該任務由哪個線程提交的就由哪個線程執(zhí)行丰涉,如圖:

    image-20211109144806090

    從源碼也可以看出,該方法直接被執(zhí)行也斯碌,也就是哪個線程提交哪個執(zhí)行一死,而不是讓線程池的線程去執(zhí)行,如圖:

    image-20211109145120665
  • 使用

    創(chuàng)建線程池使用該策略傻唾,如下:

    package com.tomato.thread.pool;
    
    import java.text.MessageFormat;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class CallerRunPolicyTest {
        public static void main(String[] args) {
            final Integer ACPU = Runtime.getRuntime().availableProcessors();
            ThreadPoolExecutor executor =
                    new ThreadPoolExecutor(
                            2 * ACPU + 1,
                            2 * ACPU + 1,
                            0,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<>(6),
                            new ThreadPoolExecutor.CallerRunsPolicy());
            for (int i = 0; i <= 100; i++) {
                executor.execute(() -> {
                    try {
                        // 模擬耗時
                        Thread.sleep(100);
                        System.out.println(MessageFormat.format("{0}執(zhí)行完成", Thread.currentThread().getName()));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
    
        }
    }
    

    最后從結果也可以得知投慈,當線程池塞滿時,也會有一些任務由主線程執(zhí)行冠骄,如下:

    image-20211109150326337

6.3 DiscardOldestPolicy

  • 說明

    當使用該策略時伪煤,線程池會丟棄最先進入阻塞隊列中的任務,給最新的任務騰出空間凛辣,如圖:

    image-20211109151124441

    當然從其源碼也可以知道抱既,先從隊列彈出一個task,再把最新的任務添加到線程池,如下

    image-20211109151448261
  • 使用

    創(chuàng)建線程池扁誓,使用該策略如下:

    package com.tomato.thread.pool;
    
    
    import java.text.MessageFormat;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class DiscardOldestPolicyTest {
        private final static AtomicInteger atomic = new AtomicInteger(1);
    
        public static void main(String[] args) {
            final Integer ACPU = Runtime.getRuntime().availableProcessors();
            ThreadPoolExecutor executor =
                    new ThreadPoolExecutor(
                            2 * ACPU + 1,
                            2 * ACPU + 1,
                            0,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<>(6),
                            new ThreadPoolExecutor.DiscardOldestPolicy());
            for (int i = 0; i <= 100; i++) {
                executor.execute(() -> {
                    try {
                        // 模擬耗時
                        Thread.sleep(100);
                        System.out.println(MessageFormat.format("{0}:執(zhí)行{1}個任務完成", Thread.currentThread().getName(), atomic.getAndIncrement()));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
    
    

    從結果也可以看出防泵,有些任務并沒有執(zhí)行而是直接被舍棄了,如下:

    pool-1-thread-18:執(zhí)行3個任務完成
    pool-1-thread-19:執(zhí)行4個任務完成
    pool-1-thread-21:執(zhí)行1個任務完成
    pool-1-thread-1:執(zhí)行15個任務完成
    pool-1-thread-17:執(zhí)行6個任務完成
    pool-1-thread-16:執(zhí)行2個任務完成
    pool-1-thread-24:執(zhí)行25個任務完成
    pool-1-thread-22:執(zhí)行23個任務完成
    pool-1-thread-13:執(zhí)行8個任務完成
    pool-1-thread-14:執(zhí)行7個任務完成
    pool-1-thread-7:執(zhí)行18個任務完成
    pool-1-thread-25:執(zhí)行19個任務完成
    pool-1-thread-23:執(zhí)行9個任務完成
    pool-1-thread-5:執(zhí)行22個任務完成
    pool-1-thread-8:執(zhí)行12個任務完成
    pool-1-thread-20:執(zhí)行5個任務完成
    pool-1-thread-6:執(zhí)行24個任務完成
    pool-1-thread-12:執(zhí)行21個任務完成
    pool-1-thread-3:執(zhí)行20個任務完成
    pool-1-thread-9:執(zhí)行10個任務完成
    pool-1-thread-10:執(zhí)行13個任務完成
    pool-1-thread-11:執(zhí)行17個任務完成
    pool-1-thread-2:執(zhí)行14個任務完成
    pool-1-thread-4:執(zhí)行16個任務完成
    pool-1-thread-15:執(zhí)行11個任務完成
    pool-1-thread-17:執(zhí)行31個任務完成
    pool-1-thread-16:執(zhí)行26個任務完成
    pool-1-thread-18:執(zhí)行30個任務完成
    pool-1-thread-1:執(zhí)行28個任務完成
    pool-1-thread-21:執(zhí)行29個任務完成
    pool-1-thread-19:執(zhí)行27個任務完成
    

6.4 DiscardPolicy

  • 說明

    使用該策略跋理,如果線程池想要拒絕任務择克,則會將最新的任務丟棄,原理如下:

    image-20211109152300372

    當然從代碼也看出,丟棄了最新任務:

    image-20211109152436923
  • 使用

    package com.tomato.thread.pool;
    
    
    import java.text.MessageFormat;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class DiscardPolicy {
    
        private final static AtomicInteger atomic = new AtomicInteger(1);
    
    
        public static void main(String[] args) {
            final Integer ACPU = Runtime.getRuntime().availableProcessors();
            ThreadPoolExecutor executor =
                    new ThreadPoolExecutor(
                            2 * ACPU + 1,
                            2 * ACPU + 1,
                            0,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<>(6),
                            new ThreadPoolExecutor.DiscardPolicy());
            for (int i = 0; i <= 100; i++) {
                executor.execute(() -> {
                    try {
                        // 模擬耗時
                        Thread.sleep(100);
                        System.out.println(MessageFormat.format("{0}:執(zhí)行{1}個任務完成", Thread.currentThread().getName(), atomic.getAndIncrement()));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
    

    從結果看出前普,有些任務并沒有執(zhí)行肚邢,而是直接被舍棄了,如下:

    pool-1-thread-21:執(zhí)行25個任務完成
    pool-1-thread-11:執(zhí)行21個任務完成
    pool-1-thread-10:執(zhí)行20個任務完成
    pool-1-thread-7:執(zhí)行4個任務完成
    pool-1-thread-16:執(zhí)行8個任務完成
    pool-1-thread-4:執(zhí)行13個任務完成
    pool-1-thread-18:執(zhí)行22個任務完成
    pool-1-thread-25:執(zhí)行7個任務完成
    pool-1-thread-12:執(zhí)行14個任務完成
    pool-1-thread-6:執(zhí)行17個任務完成
    pool-1-thread-2:執(zhí)行24個任務完成
    pool-1-thread-19:執(zhí)行18個任務完成
    pool-1-thread-13:執(zhí)行11個任務完成
    pool-1-thread-5:執(zhí)行16個任務完成
    pool-1-thread-1:執(zhí)行3個任務完成
    pool-1-thread-20:執(zhí)行10個任務完成
    pool-1-thread-8:執(zhí)行5個任務完成
    pool-1-thread-23:執(zhí)行12個任務完成
    pool-1-thread-24:執(zhí)行23個任務完成
    pool-1-thread-3:執(zhí)行15個任務完成
    pool-1-thread-17:執(zhí)行9個任務完成
    pool-1-thread-22:執(zhí)行6個任務完成
    pool-1-thread-15:執(zhí)行1個任務完成
    pool-1-thread-9:執(zhí)行19個任務完成
    pool-1-thread-14:執(zhí)行2個任務完成
    pool-1-thread-10:執(zhí)行29個任務完成
    pool-1-thread-7:執(zhí)行30個任務完成
    pool-1-thread-4:執(zhí)行31個任務完成
    pool-1-thread-21:執(zhí)行26個任務完成
    pool-1-thread-16:執(zhí)行28個任務完成
    pool-1-thread-11:執(zhí)行27個任務完成
    

6.5 sumary

四種策略拭卿,具體采用哪種策略還是需要根據具體的業(yè)務場景來實現

代碼地址: https://gitee.com/wangzh991122/thread-juc

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載骡湖,如需轉載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末峻厚,一起剝皮案震驚了整個濱河市响蕴,隨后出現的幾起案子,更是在濱河造成了極大的恐慌惠桃,老刑警劉巖浦夷,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異辜王,居然都是意外死亡劈狐,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門呐馆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肥缔,“玉大人,你說我怎么就攤上這事汹来⌒牛” “怎么了改艇?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坟岔。 經常有香客問我谒兄,道長,這世上最難降的妖魔是什么炮车? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任舵变,我火速辦了婚禮酣溃,結果婚禮上瘦穆,老公的妹妹穿的比我還像新娘。我一直安慰自己赊豌,他們只是感情好扛或,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碘饼,像睡著了一般熙兔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艾恼,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天住涉,我揣著相機與錄音,去河邊找鬼钠绍。 笑死舆声,一個胖子當著我的面吹牛,可吹牛的內容都是我干的柳爽。 我是一名探鬼主播媳握,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼磷脯!你這毒婦竟也來了蛾找?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤赵誓,失蹤者是張志新(化名)和其女友劉穎打毛,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體俩功,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡幻枉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了绑雄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片展辞。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖万牺,靈堂內的尸體忽然破棺而出罗珍,到底是詐尸還是另有隱情洽腺,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布覆旱,位于F島的核電站蘸朋,受9級特大地震影響,放射性物質發(fā)生泄漏扣唱。R本人自食惡果不足惜藕坯,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望噪沙。 院中可真熱鬧炼彪,春花似錦、人聲如沸正歼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽局义。三九已至喜爷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萄唇,已是汗流浹背檩帐。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留另萤,地道東北人湃密。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像仲墨,于是被迫代替她去往敵國和親勾缭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容