線程池

[TOC]

線程池

1. 并發(fā)隊列:阻塞隊列和非阻塞隊列

區(qū)別如下:

入隊:
  • 非阻塞隊列:當(dāng)隊列中滿了的時候,放入數(shù)據(jù),數(shù)據(jù)丟失
  • 阻塞隊列:當(dāng)隊列滿了的時候,進(jìn)行等待,什么時候隊列中有出隊的數(shù)據(jù),那么第 11 個再放進(jìn)入
出隊:
  • 非阻塞隊列:如果現(xiàn)在隊列中沒有元素,取數(shù)據(jù),得到的是 null
  • 阻塞隊列:等待,什么時候放進(jìn)去,再取出來

線程池是使用阻塞隊列。


2. 線程池原理:ThreadPoolExecutor 底層原理解析

2. 1線程的生命周期

從出生到死亡的階段。

2.2 線程池工作原理
ThreadPoolExecutor t = new ThreadPoolExecutor(1, 3, 0, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(3));

參數(shù)含義:

  • 1:核心線程數(shù)
  • 3:最大線程數(shù)
  • 0 和 TimeUnit.MICROSECONDS:當(dāng)任務(wù)量大于隊列長度需要創(chuàng)建線程的時候,新創(chuàng)建的這個線程假如把隊列的任務(wù)都執(zhí)行完了,那么等待新任務(wù)的空閑時間就是這個
  • new LinkedBlockingDeque<>(3)):阻塞隊列:
    • 方法有參數(shù):隊列長度為 3
    • 方法無參數(shù):隊列長度為 Integer.MAX_VALUE

執(zhí)行流程圖如下所示:

2.3 舉個例子
package com.test.jdk8;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3));
        // 利用線程池里的線程,開始執(zhí)行任務(wù)
        // 執(zhí)行第一個任務(wù)
        pool.execute(new TestThread());
        // 執(zhí)行下面三個任務(wù),放入阻塞隊列,阻塞隊列滿:
        pool.execute(new TestThread());
        pool.execute(new TestThread());
        pool.execute(new TestThread());

        // 執(zhí)行第 4個任務(wù),創(chuàng)建新線程,分?jǐn)側(cè)蝿?wù)
        pool.execute(new TestThread());

        // 執(zhí)行第 5 個任務(wù),創(chuàng)建新創(chuàng)建,線程數(shù)大于最大線程數(shù),報錯
        pool.execute(new TestThread());


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

}

class TestThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

3. 線程池的分類

1. newCachedThreadPool 可緩存線程池

創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。示例代碼如下:

package ThreadTest;

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

public class Test {

    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
            // lambda 表達(dá)式, 實現(xiàn) new Runnable() 的 run() 方法
            es.execute(() -> System.out.println(Thread.currentThread().getName()));
        }

        es.shutdown();

    }

}

查看 newCachedThreadPool 源碼,可知:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

該線程池的核心線程數(shù)為 0, 最大線程數(shù)為 Integer.MAX_VALUE,將會根據(jù)需要創(chuàng)建新線程,新線程的數(shù)量不大于 Integer.MAX_VALUE,線程執(zhí)行任務(wù)結(jié)束后被釋放可重復(fù)使用該線程執(zhí)行另一個任務(wù)。

部分執(zhí)行結(jié)果如下:

pool-1-thread-6
pool-1-thread-11
pool-1-thread-6
pool-1-thread-10
pool-1-thread-4
pool-1-thread-11
pool-1-thread-2
2. newFixedThreadPool 定長線程池

創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。示例代碼如下:

package ThreadTest;

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

public class Test {

    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 100; i++) {
            // lambda 表達(dá)式, 實現(xiàn) new Runnable() 的 run() 方法
            es.execute(() -> System.out.println(Thread.currentThread().getName()));
        }

        es.shutdown();

    }

}

查看 newFixedThreadPool 源碼,可知:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

傳入的參數(shù)即為核心線程數(shù)和最大線程數(shù),核心線程數(shù)和最大線程數(shù)的數(shù)量是相等的。

所以上述示例代碼的執(zhí)行結(jié)果的線程數(shù)就為 3,部分執(zhí)行結(jié)果如下:

pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
3. newScheduledThreadPool 定時線程池

創(chuàng)建一個定時線程池,支持定時及周期性任務(wù)執(zhí)行。延遲執(zhí)行示例代碼如下:

package ThreadTest;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test {

    public static void main(String[] args) {
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);

        for (int i = 0; i < 100; i++) {
            ses.schedule(() -> System.out.println(Thread.currentThread().getName()), 3, TimeUnit.SECONDS);
        }

        ses.shutdown();

    }
}

查看 newScheduledThreadPool 源碼,如下:

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

可知,newScheduledThreadPool 傳入的參數(shù)即為核心線程數(shù),最大線程數(shù)為 Integer.MAX_VALUE。

上述代碼通過設(shè)置延遲參數(shù) 3, 單位 TimeUnit.SECONDS,延遲三秒后執(zhí)行任務(wù),創(chuàng)建的線程數(shù)為 2。執(zhí)行部分結(jié)果如下:

pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
4. newSingleThreadExecutor 單例線程池

創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。示例代碼如下:

package ThreadTest;

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

public class Test {

    public static void main(String[] args) {
        ExecutorService es = Executors.newSingleThreadExecutor();

        for (int i = 0; i < 100; i++) {
            es.execute(() -> System.out.println(Thread.currentThread().getName()));
        }

        es.shutdown();

    }
}

查看 newSingleThreadExecutor 源碼,可知:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

核心線程數(shù)和最大線程數(shù)都為 1,執(zhí)行完任務(wù)后線程的存活時間為 0,即創(chuàng)建出來的線程只會執(zhí)行一次任務(wù),保證單例執(zhí)行。

運行部分結(jié)果如下:

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

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

  • 線程的兩種創(chuàng)建方式:繼承Thread類或者實現(xiàn)Runnable接口,Thread類本質(zhì)上是實現(xiàn)了Runnable接...
    繁星追逐閱讀 705評論 0 1
  • 線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務(wù)重復(fù)使用線程,線程創(chuàng)建的開銷就被分?jǐn)偟搅硕鄠€任...
    安仔夏天勤奮閱讀 1,125評論 0 10
  • 前言 掌握線程池是后端程序員的基本要求,相信大家求職面試過程中,幾乎都會被問到有關(guān)于線程池的問題。我在網(wǎng)上搜集了幾...
    Jay_Wei閱讀 1,059評論 0 0
  • 前言 掌握線程池是后端程序員的基本要求,相信大家求職面試過程中,幾乎都會被問到有關(guān)于線程池的問題。我在網(wǎng)上搜集了幾...
    勤奮的碼農(nóng)閱讀 1,390評論 0 1
  • 姥姥沒在家,昨晚回來簡單的打掃了一下房間,我們就睡覺了。 你和爸爸一覺睡到九點鐘,我們一起去喝豆腐湯。你好久沒喝了...
    素面迎風(fēng)閱讀 157評論 0 0

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