Java多线程与高并发七本来想写ThreadLocal源码的,实在是看底层代码看不动了!先搁置吧。Java多线程与高并发八给同学们分享下面试中常考的线程池的七大参数!
防止频繁创建和销毁线程,让每个线程可以多次使用,防止消耗过多内存,所以我们使用线程池。
像下面这样定义线程池,不香吗?为什么不用呢?
ExecutorService executorService = Executors.newFixedThreadPool(5);
阿里的《阿里巴巴 Java 开发手册》已经说得很清楚明白了,我这里就直接盗用吧。
自己创建线程池的目的是让我们自己更加明确线程池的运行规则!
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//实现略
}
corePoolSize:线程池持有的可用的线程数量 maximumPoolSize:当队列workQueue满了过后,线程池可用的最大线程数量 keepAliveTime:大于核心线程数的帮工线程们等待新任务到来的等待时间,超过该时间,帮工线程们还没有可执行的任务,这些线程就会关闭unit:上个参数keepAliveTime的单位,通常是以,TimeUnit.SECONDS,这样给出的,表示秒 workQueue:任务堆积的等待队列,比如当前核心线程数是5,来了10个,那么后来的5个就会放到该队列中 threadFactory:线程工厂,主要是给线程赋予特定名称 handler:拒绝策略,当队列满了,帮工线程们也起来了,还在往线程池里加线程该怎么处理?这个策略就是定义怎么处理的。
import java.util.concurrent.*;
/**
* 线程池测试
*
* @author zab
* @date 2020-01-21 22:40
*/
public class ThreadPoolTest {
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 8;
private static final int KEEP_ALIVE_TIME = 60;
public static void main(String[] args) throws Exception{
ThreadPoolTest t = new ThreadPoolTest();
ThreadPoolExecutor e = t.getThreadPool2();
for (int i = 0; i < 100; i++) {
e.execute(t.new MyRunnable(i));
}
}
private ThreadPoolExecutor getThreadPool0() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new MyThreadFactory("whatBusiness"),
new ThreadPoolExecutor.AbortPolicy()
);
return threadPoolExecutor;
}
private ThreadPoolExecutor getThreadPool1() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new MyThreadFactory("whatBusiness"),
new ThreadPoolExecutor.DiscardPolicy()
);
return threadPoolExecutor;
}
private ThreadPoolExecutor getThreadPool2() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new MyThreadFactory("whatBusiness"),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
return threadPoolExecutor;
}
private ThreadPoolExecutor getThreadPool3() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new MyThreadFactory("whatBusiness"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
return threadPoolExecutor;
}
private ThreadPoolExecutor getThreadPool4() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
new MyThreadFactory("whatBusiness"),
new MyRejectedExecutionHandler()
);
return threadPoolExecutor;
}
class MyRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("做自己想要的逻辑");
System.out.println("核心池大小:" + executor.getCorePoolSize());
}
}
class MyThreadFactory implements ThreadFactory {
private String threadName;
public MyThreadFactory(String threadName) {
this.threadName = threadName;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName(threadName+t.getName());
return t;
}
}
class MyRunnable implements Runnable {
int count = 0;
public MyRunnable(int count){
this.count = count;
}
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread---"+Thread.currentThread().getName()+"----count:"+count);
}
}
}
这里定义getThreadPool的几个方法主要是获取不同拒绝策略的线程池:
getThreadPool0:获取拒绝策略的线程池 getThreadPool1:获取丢弃策略的线程池 getThreadPool2:获取丢弃最老策略的线程池 getThreadPool3:获取调用者执行策略的线程池 getThreadPool4:获取自定义策略的线程池
然后自定义了几个内部类:
MyRejectedExecutionHandler自定义拒绝策略 MyThreadFactory自定义线程工厂 MyRunnable自定义线程类
main方法很简单,就是获取不同的线程池,for循环跑100次,然后撑爆线程池的等待队列,测试不同拒绝策略的表现。
1、抛异常策略
可以看到核心线程数5,最大线程数8,循环执行100次,当核心线程数满了后,再来新的线程,就会往等待队列里放,这里的等待队列是一个ArrayBlockingQueue,容量是10,再来线程满了后,会创建【帮工线程】,可以看到线程名最大是7,说明该测试已经把【帮工线程】都用上了,再来线程就会执行拒绝策略,可以看到控制台有抛异常。
2、丢弃策略
丢弃策略什么都不干,其他分析同拒绝策略,值得注意的是,控制台5、6、7线程对应的计数为什么是15、16、17。设想一个场景:
银行有八个窗口,其中五个是打开状态,有人络绎不绝的来办理业务,一个、两个、三个...当第五个人来的时候,如果前面的人都未办理完业务,那么第五个人一定走向最后一个打开的窗口,这时候又来了第六个、第七个...这个时候,保安见状,略有所思:今儿个业务太好了,得让客户排队了!最后第十五个人来的时候,前面都还没人办理完业务,排队都排了10个人了。。。第十六个人来的时候,保安终于坐不住了,去通知大堂经理,大堂经理见状,立即安排了三个业务员把剩余的窗口打开,保安就安排第十六、十七、十八个客户去了新开的窗口办理业务了,而排队的10个人依旧排着队。
以上场景看似不合理,但是确实是代码执行中看到的现象的形象描述。
3、丢弃最老等待线程策略
这个可以看到,等待最久的线程都被忽略了,新来的得到执行。控制台对应的count是for循环的下标,最后执行的下标都达到99了,这说明循环中比较小的数,例如第77次、第88次这些都是等待比较久的线程,都已经被丢弃了。
结合上述案例来说,就好比先来的等待办理业务的客户等不及了,离开银行了。
4、谁调用谁执行策略
这个比较简单,就是main方法调用的线程池执行的,那么线程池的线程跑不完的就给调用者main方法执行。
5、自定义策略
这个可以看到,一旦有新的线程既排不了队,又不能执行,就会执行自定义的策略。这里没有策略,只是输出信息。