1. ThreadPoolExecutor参数解读
- corePoolSize:核心线程数,指定线程池中核心的线程数量(也可以理解为最少的线程个数),即使这些线程处于空闲状态,也不会被销毁,除非设置了allowCoreThreadTimeOut(true)。默认情况下,创建线程池后,线程池中是没有线程的,需要提交任务才会创建线程。在实际的使用过程中,如果需要在创建了线程池后立即创建线程,可以选用下面两种方式:
-
- prestartCoreThread:初始化一个核心线程
- prestartAllCoreThreads:初始化所有核心线程
- maximumPoolSize:指定线程池中最大的线程数量,当核心线程全部繁忙 且 任务队列满了后,线程池中会追加新线程,并且数量达到maximumPoolSize的数量。
- BlockingQueue: 任务队列.当核心线程全部繁忙后,由submit/execute方法提交的Runnable任务存放到任务队列中,等待核心线程的执行。
- keepAliveTime:线程存活时间,如果当前的线程数量大于核心线程数量,超过了存活时间,非核心线程则会被销毁。若设置了allowCoreThreadTimeOut 为 true 的情况下,核心线程也会被销毁。
- ThreadFactory:线程工厂,用于创建线程,也可以自定义线程工厂;
-
- Executors.defaultThreadFactory():用于创建普通的线程,具有标准的访问线程访问权限。
- Executors.privilegedThreadFactory():用户创建带有特权的线程,这些线程能够加载一些系统属性和库。
- RejectedExecutionHandler():拒绝策略。当核心线程全在繁忙,任务队列已满,并且最大线程数量已经等于当前线程的数量时,就会发生拒绝执行任务。下面介绍了四种拒绝策略:
-
- AbortPolicy(默认):丢弃任务抛出RejectedExecutionException 异常。
- DiscardPolicy:直接丢弃任务,不抛出异常。
- DiscardOldestPolicy:丢弃任务队列中最靠前的任务,并且执行当前任务。
- CallerRunsPolicy:交由任务的调度线程(提交该任务的线程)来执行.
- 除了上面四种拒绝策略还可以实现 RejectedExecutionHandler 来自定义拒绝策略。
package org.sj;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.sj.collection.pool.MyRejectHandler;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.*;
@SpringBootTest
@Slf4j
class InterviewApplicationTests {
//公用线程池对象
private static ThreadPoolExecutor threadPoolExecutor;
public static void init(int coreSize,
int maxSize,
int aliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler){
threadPoolExecutor = new ThreadPoolExecutor(
coreSize,
maxSize,
aliveTime,
unit,
workQueue,
threadFactory,
handler
);
}
/**
*拒绝策略-->AbortPolicy
*拒绝任务并且抛出异常
*/
@Test
void text1(){
init(1,
1,
1,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.privilegedThreadFactory(),
//拒绝策略
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 3; i++) {
threadPoolExecutor.execute(new Thread(){
@Override
public void run() {
log.info("{} is running", Thread.currentThread().getName());
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
/**
* 拒绝策略 --> DiscardPolicy
* 拒绝任务并且不会抛出异常
*/
@Test
void text2(){
init(1,
1,
1,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.privilegedThreadFactory(),
//拒绝策略
new ThreadPoolExecutor.DiscardPolicy());
try {
for (int i = 0; i < 3; i++) {
threadPoolExecutor.execute(new Thread(){
@Override
public void run() {
log.info("{} is running", Thread.currentThread().getName());
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
/**
* 拒绝策略 --> DiscardOldestPolicy
* 丢弃任务队列中最靠前的任务,执行当前任务
* 正确结果应该是1被丢掉了
*/
@Test
void test3(){
init(1,
1,
1,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.privilegedThreadFactory(),
//拒绝策略
new ThreadPoolExecutor.DiscardOldestPolicy());
try {
for (int i = 0; i < 3; i++) {
int finalI = i;
threadPoolExecutor.execute(new Thread(){
@Override
public void run() {
log.info("{} is running--> {}", Thread.currentThread().getName(), finalI);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
/**
* 拒绝策略 --> CallerRunsPolicy
* 把任务交给提供任务的线程去执行
* 正确结果应是2交给主线程去执行了
*/
@Test
void test4(){
init(1,
1,
1,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.privilegedThreadFactory(),
//拒绝策略
new ThreadPoolExecutor.CallerRunsPolicy());
try {
for (int i = 0; i < 3; i++) {
int finalI = i;
threadPoolExecutor.execute(new Thread(){
@Override
public void run() {
log.info("{} is running--> {}", Thread.currentThread().getName(), finalI);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
/**
* 自定义拒绝策略 --> 若任务队列满了,则等待两秒
* 正确结果应该是都会执行
*/
@Test
void test5(){
init(1,
1,
1,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.privilegedThreadFactory(),
//自定义拒绝策略
new MyRejectHandler());
try {
for (int i = 0; i < 3; i++) {
int finalI = i;
threadPoolExecutor.execute(new Thread(){
@Override
public void run() {
log.info("{} is running--> {}", Thread.currentThread().getName(), finalI);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
}
/**
* 自定义拒绝策略 --> 若当前任务队列已经满了,等待2秒
*/
@Slf4j
public class MyRejectHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().offer(r,2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
2. 工作中如何创建线程池?
- 个人认为下面这种好一点,可以通过 Bean 中的 name 属性指定不同的线程池引入;下面直接上代码了;
@Configuration
public class ThreadPoolConfig {
@Bean(name = "threadPool1")
public ThreadPoolExecutor threadPoolExecutor(){
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
2,
2,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
return executor;
}
}
- 关于在业务中应该分开使用多个线程池还是共用单个线程池的问题?
个人认为,应该不同的业务对应不同的线程池,可以有业务隔离的作用,并且防止一个线程池中线程阻塞或者占满时导致的其他业务的执行,其次,业务之间的特征是不一样的,有些主要消耗 CPU,有些主要依靠 IO,有些任务时间 长....等等;