說一下線程池的核心參數(shù),線程池的執(zhí)行原理
1.線程池的核心參數(shù)
線程池七大核心參數(shù)如下所示:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)- corePoolSize: 核心線程數(shù)目
- maximumPoolSize: 最大線程數(shù)目 = (核心線程+救急線程的最大數(shù)目)
- keepAliveTime: 生存時間 - 救急線程的生存時間,生存時間內(nèi)沒有新任務(wù),此線程資源會釋放
- unit: 時間單位 , 救急線程的生存時間單位,如秒、毫秒等
- workQueue: 當(dāng)沒有空閑核心線程時,新來任務(wù)會加入到此隊列排隊,隊列滿會創(chuàng)建救急線程執(zhí)行任務(wù)
- **threadFactory: 線程工廠 , 可以定制線程對象的創(chuàng)建,例如設(shè)置線程名字、是否是守護線程等
- handler 拒絕策略 - 當(dāng)所有線程都在繁忙,workQueue 也放滿時,會觸發(fā)拒絕策略
思考:線程池的執(zhí)行原理知道嘛?
2. 線程池的執(zhí)行原理
1: 任務(wù)在提交的時候,首先判斷核心線程數(shù)是否已滿,如果沒有滿則直接添加到工作線程執(zhí)行
2: 如果核心線程數(shù)滿了,則判斷阻塞隊列是否已滿,如果沒有滿,當(dāng)前任務(wù)存入阻塞隊列
3:如果阻塞隊列也滿了,則判斷線程數(shù)是否小于最大線程數(shù),如果滿足條件,則使用臨時線程執(zhí)行任務(wù)如果核心或臨時線程執(zhí)行完成任務(wù)后會檢查阻塞隊列中是否有需要執(zhí)行的線程,如果有,則使用非核心線程執(zhí)行任務(wù)
4:如果所有線程都在忙著(核心線程+臨時線程),則走拒絕策略

思考:拒絕策略有哪些?
1.AbortPolicy:直接拋出異常,默認策略
2.CallerRunsPolicy:用調(diào)用者所在的線程來執(zhí)行任務(wù)
3.DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務(wù),并執(zhí)行當(dāng)前任務(wù)
4.DiscardPolicy:直接丟棄任務(wù)
案例:
public class TestThreadPoolExecutor {
static class MyTask implements Runnable {
private final String name;
private final long duration;
public MyTask(String name) {
this(name, 0);
}
public MyTask(String name, long duration) {
this.name = name;
this.duration = duration;
}
@Override
public void run() {
try {
LoggerUtils.get("myThread").debug("running..." + this);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "MyTask(" + name + ")";
}
}
public static void main(String[] args) throws InterruptedException {
AtomicInteger c = new AtomicInteger(1);
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
3,
0,
TimeUnit.MILLISECONDS,
queue,
r -> new Thread(r, "myThread" + c.getAndIncrement()),
new ThreadPoolExecutor.AbortPolicy());
showState(queue, threadPool);
threadPool.submit(new MyTask("1", 3600000));
showState(queue, threadPool);
threadPool.submit(new MyTask("2", 3600000));
showState(queue, threadPool);
threadPool.submit(new MyTask("3"));
showState(queue, threadPool);
threadPool.submit(new MyTask("4"));
showState(queue, threadPool);
threadPool.submit(new MyTask("5",3600000));
showState(queue, threadPool);
threadPool.submit(new MyTask("6"));
showState(queue, threadPool);
}
private static void showState(ArrayBlockingQueue<Runnable> queue, ThreadPoolExecutor threadPool) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Object> tasks = new ArrayList<>();
for (Runnable runnable : queue) {
try {
Field callable = FutureTask.class.getDeclaredField("callable");
callable.setAccessible(true);
Object adapter = callable.get(runnable);
Class<?> clazz = Class.forName("java.util.concurrent.Executors$RunnableAdapter");
Field task = clazz.getDeclaredField("task");
task.setAccessible(true);
Object o = task.get(adapter);
tasks.add(o);
} catch (Exception e) {
e.printStackTrace();
}
}
LoggerUtils.main.debug("pool size: {}, queue: {}", threadPool.getPoolSize(), tasks);
}
}思考:線程池中有哪些常見的阻塞隊列?
3. 常見阻塞隊列
workQueue: 當(dāng)沒有空閑核心線程時,新來任務(wù)會加入到此隊列排隊,隊列滿會創(chuàng)建救急線程執(zhí)行任務(wù)。
比較常見的有4個,用的最多是ArrayBlockingQueue和LinkedBlockingQueue。
- 1.ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊列
- 2.LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的有界阻塞隊列
- 3.DelayedWorkQueue :是一個優(yōu)先級隊列,它可以保證每次出隊的任務(wù)都是當(dāng)前隊列中執(zhí)行時間最靠前的
- 4.SynchronousQueue:不存儲元素的阻塞隊列,每個插入操作都必須等待一個移出操作
3.1. ArrayBlockingQueue的LinkedBlockingQueue區(qū)別
inkedBlockingQueue** | ArrayBlockingQueue |
默認無界,支持有界 | 強制有界 |
底層是鏈表 | 底層是數(shù)組 |
是懶惰的,創(chuàng)建節(jié)點的時候添加數(shù)據(jù) | 提前初始化 Node 數(shù)組 |
入隊會生成新 Node | Node需要是提前創(chuàng)建好的 |
兩把鎖(頭尾) | 一把鎖 |
左邊是LinkedBlockingQueue加鎖的方式,右邊是ArrayBlockingQueue加鎖的方式

- LinkedBlockingQueue:讀和寫各有一把鎖,性能相對較好
- ArrayBlockingQueue:只有一把鎖,讀和寫公用,性能相對于LinkedBlockingQueue差一些
4. 線程池的種類有哪些
在java.util.concurrent.Executors類中提供了大量創(chuàng)建連接池的靜態(tài)方法,常見就有四種
1.創(chuàng)建使用固定線程數(shù)的線程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
}- 核心線程數(shù)與最大線程數(shù)一樣,沒有救急線程
- 阻塞隊列是LinkedBlockingQueue,最大容量為Integer.MAX_VALUE
- 適用場景:適用于任務(wù)量已知,相對耗時的任務(wù)
- 案例
/**
* @author springboot葵花寶典
* @description: TODO
*/
public class FixedThreadPoolTest {
static class FixedThreadDemo implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 2; i++) {
System.out.println(name + ":" + i);
}
}
}
public static void main(String[] args) throws InterruptedException {
//創(chuàng)建一個固定大小的線程池,核心線程數(shù)和最大線程數(shù)都是3
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executorService.submit(new FixedThreadDemo());
Thread.sleep(10);
}
executorService.shutdown();
}
}2.單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任 務(wù),保證所有任務(wù)按照指定順序(FIFO)執(zhí)行
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()));
}- 核心線程數(shù)和最大線程數(shù)都是1
- 阻塞隊列是LinkedBlockingQueue,最大容量為Integer.MAX_VALUE
- 適用場景:適用于按照順序執(zhí)行的任務(wù)
- 案例
/**
* @author springboot葵花寶典
* @description: TODO
*/
public class NewSingleThreadTest {
static int count = 0;
static class Demo implements Runnable {
@Override
public void run() {
count++;
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
public static void main(String[] args) throws InterruptedException {
//單個線程池,核心線程數(shù)和最大線程數(shù)都是1
ExecutorService exec = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
exec.execute(new Demo());
Thread.sleep(5);
}
exec.shutdown();
}
}- 可緩存線程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS, n
ew SynchronousQueue<>());
}- 核心線程數(shù)為0
- 最大線程數(shù)是Integer.MAX_VALUE
- 阻塞隊列為SynchronousQueue:不存儲元素的阻塞隊列,每個插入操作都必須等待一個移出操作
- 適用場景:適合任務(wù)數(shù)比較密集,但每個任務(wù)執(zhí)行時間較短的情況
- 案例:
/**
* @author springboot葵花寶典
* @description: TODO
*/
public class CachedThreadPoolTest {
static class Demo implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
try {
//修改睡眠時間,模擬線程執(zhí)行需要花費的時間
Thread.sleep(100);
System.out.println(name + "執(zhí)行完了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
//創(chuàng)建一個緩存的線程,沒有核心線程數(shù),最大線程數(shù)為Integer.MAX_VALUE
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
exec.execute(new Demo());
Thread.sleep(1);
}
exec.shutdown();
}
}4.提供了“延遲”和“周期執(zhí)行”功能的ThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler{
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler);
}- 適用場景:有定時和延遲執(zhí)行的任務(wù)
- 案例
/**
* @author springboot葵花寶典
* @description: TODO
*/
public class ScheduledThreadPoolTest {
static class Task implements Runnable {
@Override
public void run() {
try {
String name = Thread.currentThread().getName();
System.out.println(name + ", 開始:" + new Date());
Thread.sleep(1000);
System.out.println(name + ", 結(jié)束:" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
//按照周期執(zhí)行的線程池,核心線程數(shù)為2,最大線程數(shù)為Integer.MAX_VALUE
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
System.out.println("程序開始:" + new Date());
/**
* schedule 提交任務(wù)到線程池中
* 第一個參數(shù):提交的任務(wù)
* 第二個參數(shù):任務(wù)執(zhí)行的延遲時間
* 第三個參數(shù):時間單位
*/
scheduledThreadPool.schedule(new Task(), 0, TimeUnit.SECONDS);
scheduledThreadPool.schedule(new Task(), 1, TimeUnit.SECONDS);
scheduledThreadPool.schedule(new Task(), 5, TimeUnit.SECONDS);
Thread.sleep(5000);
// 關(guān)閉線程池
scheduledThreadPool.shutdown();
}
}5. 線程池面試題
面試官:線程池的核心參數(shù)有哪些?
候選人:
在線程池中一共有7個核心參數(shù):
- corePoolSize 核心線程數(shù)目 - 池中會保留的最多線程數(shù)
- maximumPoolSize 最大線程數(shù)目 - 核心線程+救急線程的最大數(shù)目
- keepAliveTime 生存時間 - 救急線程的生存時間,生存時間內(nèi)沒有新任務(wù),此線程資源會釋放
- unit 時間單位 - 救急線程的生存時間單位,如秒、毫秒等
- workQueue - 當(dāng)沒有空閑核心線程時,新來任務(wù)會加入到此隊列排隊,隊列滿會創(chuàng)建救急線程執(zhí)行任務(wù)
- threadFactory 線程工廠 - 可以定制線程對象的創(chuàng)建,例如設(shè)置線程名字、是否是守護線程等
- handler 拒絕策略 - 當(dāng)所有線程都在繁忙,workQueue 也放滿時,會觸發(fā)拒絕策略
在拒絕策略中又有4中拒絕策略
- 第一種是AbortPolicy,之際拋異常
- 第二種是CallerRunsPolicy由調(diào)用者執(zhí)行任務(wù)
- 第三是DiscardOldestPolicy丟棄當(dāng)前的任務(wù)
- 第四是DiscardPolicy丟棄最早排隊任務(wù)。默認是直接拋異常。
面試官:線程池的執(zhí)行原理知道嗎?
候選人:
首先判斷線程池里的核心線程是否都在執(zhí)行任務(wù),如果不是則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果核心線程都在執(zhí)行任務(wù),則線程池判斷工作隊列是否已滿,如果工作隊列沒有滿,則將新提交的任務(wù)存儲在這個工作隊 列里。如果工作隊列滿了,則判斷線程池里的線程是否都處于工作狀態(tài),如果沒有,則創(chuàng)建一個新的工作線程來執(zhí)行任 務(wù)。如果已經(jīng)滿了,則交給拒絕策略來處理這個任務(wù)。
面試官:線程池的種類有哪些?
候選人:
在jdk中默認提供了4中方式創(chuàng)建線程池
- 第一個是:newCachedThreadPool創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回 收空閑線程,若無可回收,則新建線程。
- 第二個是:newFixedThreadPool 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列 中等待。
- 第三個是:newScheduledThreadPool 創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行。
- 第四個是:newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任 務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。


























