最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java線程池如何統(tǒng)計(jì)線程空閑時(shí)間

 更新時(shí)間:2022年11月18日 10:48:47   作者:一燈架構(gòu)  
這篇文章主要和大家分享一個(gè)面試題:Java線程池是怎么統(tǒng)計(jì)線程空閑時(shí)間?文中的示例代碼講解詳細(xì),對(duì)我們掌握J(rèn)ava有一定幫助,需要的可以參考一下

背景介紹

你剛從學(xué)校畢業(yè)后,到新公司實(shí)習(xí),試用期又被畢業(yè),然后你又不得不出來面試,好在面試的時(shí)候碰到個(gè)美女面試官!

面試官: 小伙子,我看你簡歷上寫的項(xiàng)目中用到了線程池,你知道線程池是怎樣實(shí)現(xiàn)復(fù)用線程的?

這面試官是不是想坑我?是不是擺明了不讓我通過?

難道你不應(yīng)該問線程池有哪些核心參數(shù)?每個(gè)參數(shù)具體作用是什么?

往線程池中不斷提交任務(wù),線程池的處理流程是什么?

這些才是你應(yīng)該問的,這些八股文我已經(jīng)背熟了,你不問,瞎問什么復(fù)用線程?

幸虧我看了一燈的八股文,聽我給你背一遍!

我: 線程池復(fù)用線程的邏輯很簡單,就是在線程啟動(dòng)后,通過while死循環(huán),不斷從阻塞隊(duì)列中拉取任務(wù),從而達(dá)到了復(fù)用線程的目的。

具體源碼如下:

// 線程執(zhí)行入口
public void run() {
    runWorker(this);
}

// 線程運(yùn)行核心方法
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock();
    boolean completedAbruptly = true;
    try {
        // 1. 使用while死循環(huán),不斷從阻塞隊(duì)列中拉取任務(wù)
        while (task != null || (task = getTask()) != null) {
            // 加鎖,保證thread不被其他線程中斷(除非線程池被中斷)
            w.lock();
            // 2. 校驗(yàn)線程池狀態(tài),是否需要中斷當(dāng)前線程
            if ((runStateAtLeast(ctl.get(), STOP) ||
                    (Thread.interrupted() &&
                            runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 3. 執(zhí)行run方法
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x;
                    throw x;
                } catch (Error x) {
                    thrown = x;
                    throw x;
                } catch (Throwable x) {
                    thrown = x;
                    throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

runWorker方法邏輯很簡單,就是不斷從阻塞隊(duì)列中拉取任務(wù)并執(zhí)行。

面試官: 小伙子,有點(diǎn)東西。我們都知道線程池會(huì)回收超過空閑時(shí)間的線程,那么線程池是怎么統(tǒng)計(jì)線程的空閑時(shí)間的?

美女面試官的問題真刁鉆,讓人頭疼啊!這問的也太深了吧!

沒看過源碼的話,真不好回答。

我: 嗯...,可能是有個(gè)監(jiān)控線程在后臺(tái)不停的統(tǒng)計(jì)每個(gè)線程的空閑時(shí)間,看到線程的空閑時(shí)間超過閾值的時(shí)候,就回收掉。

面試官: 小伙子,你的想法挺不錯(cuò),邏輯很嚴(yán)謹(jǐn),你確定線程池內(nèi)部是這么實(shí)現(xiàn)的嗎?

問得我有點(diǎn)不自信了,沒看過源碼不能瞎蒙。

我還是去瞅一眼一燈寫的八股文吧。

我: 這個(gè)我知道,線程池統(tǒng)計(jì)線程的空閑時(shí)間的實(shí)現(xiàn)邏輯很簡單。

阻塞隊(duì)列(BlockingQueue)提供了一個(gè)poll(time, unit)方法用來拉取數(shù)據(jù),

作用就是: 當(dāng)隊(duì)列為空時(shí),會(huì)阻塞指定時(shí)間,然后返回null。

線程池就是就是利用阻塞隊(duì)列的這個(gè)方法,如果在指定時(shí)間內(nèi)拉取不到任務(wù),就表示該線程的存活時(shí)間已經(jīng)超過閾值了,就要被回收了。

具體源碼如下:

// 從阻塞隊(duì)列中拉取任務(wù)
private Runnable getTask() {
    boolean timedOut = false;
    for (; ; ) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 1. 如果線程池已經(jīng)停了,或者阻塞隊(duì)列是空,就回收當(dāng)前線程
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);
        // 2. 再次判斷是否需要回收線程
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        try {
            // 3. 在指定時(shí)間內(nèi),從阻塞隊(duì)列中拉取任務(wù)
            Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
            if (r != null)
                return r;
          	// 4. 如果沒有拉取到任務(wù),就標(biāo)識(shí)該線程已超時(shí),然后就被回收
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

面試官: 小伙子,可以啊,你是懂線程池源碼的。再問你個(gè)問題,如果線程池拋異常了,也沒有try/catch,會(huì)發(fā)生什么?

美女面試官你這是準(zhǔn)備打破砂鍋問到底,鐵了心不讓我過,是吧?

我的代碼風(fēng)格是很嚴(yán)謹(jǐn)?shù)?,誰寫的業(yè)務(wù)代碼不try/catch,也沒遇到過這種情況。

讓我再看一下一燈總結(jié)的八股文吧。

我: 有了,線程池中的代碼如果拋異常了,也沒有try/catch,會(huì)從線程池中刪除這個(gè)異常線程,并創(chuàng)建一個(gè)新線程。

不信的話,我們可以測試驗(yàn)證一下:

/**
 * @author 一燈架構(gòu)
 * @apiNote 線程池示例
 **/
public class ThreadPoolDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        // 1. 創(chuàng)建一個(gè)單個(gè)線程的線程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        // 2. 往線程池中提交3個(gè)任務(wù)
        for (int i = 0; i < 3; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 關(guān)注公眾號(hào):一燈架構(gòu)");
                throw new RuntimeException("拋異常了!");
            });
        }

        // 3. 關(guān)閉線程池
        executorService.shutdown();
    }

}

輸出結(jié)果:

pool-1-thread-1 關(guān)注公眾號(hào):一燈架構(gòu)
pool-1-thread-2 關(guān)注公眾號(hào):一燈架構(gòu)
pool-1-thread-3 關(guān)注公眾號(hào):一燈架構(gòu)
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 拋異常了!
    at com.yideng.SynchronousQueueDemo.lambda$main$0(ThreadPoolDemo.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-2" java.lang.RuntimeException: 拋異常了!
    at com.yideng.SynchronousQueueDemo.lambda$main$0(ThreadPoolDemo.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-3" java.lang.RuntimeException: 拋異常了!
    at com.yideng.SynchronousQueueDemo.lambda$main$0(ThreadPoolDemo.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

從輸出結(jié)果中可以看出,線程名稱并不是同一個(gè),而是累加的,說明原線程已經(jīng)被回收,新建了個(gè)線程。

我們再看一下源碼,驗(yàn)證一下:

// 線程拋異常后,退出邏輯
private void processWorkerExit(ThreadPoolExecutor.Worker w, boolean completedAbruptly) {
    if (completedAbruptly)
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        // 1. 從工作線程中刪除當(dāng)前線程
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    // 2. 中斷當(dāng)前線程
    tryTerminate();

    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && !workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 3. 新建一個(gè)線程
        addWorker(null, false);
    }
}

如果想統(tǒng)一處理異常,可以自定義線程創(chuàng)建工廠,在工廠里面設(shè)置異常處理邏輯。

/**
 * @author 一燈架構(gòu)
 * @apiNote 線程池示例
 **/
public class ThreadPoolDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        // 1. 創(chuàng)建一個(gè)單個(gè)線程的線程池
        ExecutorService executorService = Executors.newSingleThreadExecutor(runnable -> {
            // 2. 自定義線程創(chuàng)建工廠,并設(shè)置異常處理邏輯
            Thread thread = new Thread(runnable);
            thread.setUncaughtExceptionHandler((t, e) -> {
                System.out.println("捕獲到異常:" + e.getMessage());
            });
            return thread;
        });

        // 3. 往線程池中提交3個(gè)任務(wù)
        for (int i = 0; i < 3; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 關(guān)注公眾號(hào):一燈架構(gòu)");
                throw new RuntimeException("拋異常了!");
            });
        }

        // 4. 關(guān)閉線程池
        executorService.shutdown();
    }

}

輸出結(jié)果:

Thread-0 關(guān)注公眾號(hào):一燈架構(gòu)
捕獲到異常:拋異常了!
Thread-1 關(guān)注公眾號(hào):一燈架構(gòu)
捕獲到異常:拋異常了!
Thread-2 關(guān)注公眾號(hào):一燈架構(gòu)
捕獲到異常:拋異常了!

以上就是詳解Java線程池如何統(tǒng)計(jì)線程空閑時(shí)間的詳細(xì)內(nèi)容,更多關(guān)于Java統(tǒng)計(jì)線程空閑時(shí)間的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Spring boot/Spring 統(tǒng)一錯(cuò)誤處理方案的使用

    詳解Spring boot/Spring 統(tǒng)一錯(cuò)誤處理方案的使用

    這篇文章主要介紹了詳解Spring boot/Spring 統(tǒng)一錯(cuò)誤處理方案的使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • Spring Boot 中啟用定時(shí)任務(wù)的操作方法

    Spring Boot 中啟用定時(shí)任務(wù)的操作方法

    文章主要介紹了如何在Spring Boot中啟用定時(shí)任務(wù),包括使用@EnableScheduling注解、配置項(xiàng)控制定時(shí)任務(wù)是否開啟以及如何關(guān)閉cron定時(shí)任務(wù),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • java判斷字符串String是否為空問題淺析

    java判斷字符串String是否為空問題淺析

    這篇文章主要介紹了java判斷字符串String是否為空問題,有需要的朋友可以參考一下
    2014-01-01
  • 記一次公司JVM堆溢出抽絲剝繭定位的過程解析

    記一次公司JVM堆溢出抽絲剝繭定位的過程解析

    這篇文章主要介紹了記一次公司JVM堆溢出抽絲剝繭定位的過程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • @PathVariable、@RequestParam和@RequestBody的區(qū)別

    @PathVariable、@RequestParam和@RequestBody的區(qū)別

    本文主要介紹了@PathVariable、@RequestParam和@RequestBody的區(qū)別和使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Redis打開rdb文件常用方法詳解

    Redis打開rdb文件常用方法詳解

    這篇文章主要介紹了Redis打開rdb文件常用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java的LinkedHashSet源碼深入講解

    Java的LinkedHashSet源碼深入講解

    這篇文章主要介紹了Java的LinkedHashSet源碼深入講解,LinkedHashSet是HashSet的子類,而由于HashSet實(shí)現(xiàn)了Set接口,因此LinkedHashSet也間接實(shí)現(xiàn)了Set類,LinkedHashSet類屬于java.base模塊,java.util包下,需要的朋友可以參考下
    2023-09-09
  • Java Spring boot 2.0 跨域問題的解決

    Java Spring boot 2.0 跨域問題的解決

    本篇文章主要介紹了Java Spring boot 2.0 跨域問題的解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04
  • springmvc九大組件之HandlerAdapter詳解

    springmvc九大組件之HandlerAdapter詳解

    這篇文章主要介紹了springmvc九大組件之HandlerAdapter詳解,RequestMappingHandlerAdapter支持的handler的類型是HandlerMethod,而HandlerMethod是通過解析@RequestMapping注解獲得的,需要的朋友可以參考下
    2023-11-11
  • SpringBoot使用Mybatis&Mybatis-plus文件映射配置方法

    SpringBoot使用Mybatis&Mybatis-plus文件映射配置方法

    這篇文章主要介紹了SpringBoot使用Mybatis&Mybatis-plus文件映射配置方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-05-05

最新評(píng)論

北川| 峨眉山市| 新宁县| 绥芬河市| 元谋县| 电白县| 墨脱县| 利川市| 克什克腾旗| 上饶市| 洛宁县| 临泽县| 长子县| 尼玛县| 革吉县| 酒泉市| 安吉县| 镇安县| 安达市| 德格县| 仙居县| 吉水县| 九龙坡区| 鄂托克旗| 隆回县| 射阳县| 沿河| 铜川市| 吉安县| 水富县| 凤山县| 南漳县| 遂宁市| 郯城县| 新野县| 乳山市| 井冈山市| 睢宁县| 太白县| 包头市| 当雄县|