1.传统方式创建线程池的弊端
1.传统方式通过 ExecutorService executorService = Executors.newCachedThreadPool() 创建线程池 虽然简化了开发人员的创建流程 但是由于这种方式隐藏了线程池内部的原理 在线上可能存在很大的风险 故需要手动创建线程池(默认的方式 线程池采用队列没有指定大小 采用无界队列Integer.MAX 在高并发环境下 容易导致服务器资源枯竭)
2.推荐创建方式
/**
* 方式1 阿里推荐方式
*/
private ThreadFactory threadFactory=new ThreadFactoryBuilder()
.setNameFormat("longyi-")
.build();
ThreadPoolExecutor executor=new ThreadPoolExecutor(20,30,0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),threadFactory,new ThreadPoolExecutor.AbortPolicy());
@Bean("asyncExecute")
public Executor asyncExecute() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 配置缓存队列数
executor.setQueueCapacity(20);
// 配置最大空闲时间
executor.setKeepAliveSeconds(60);
// 前缀
executor.setThreadNamePrefix("stock-thread-");
// 线程池对拒绝任务的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
return executor;
}
3. 线程池拒绝策略
(1)ThreadPoolExecutor.AbortPolicy 丢弃任务,并抛出 RejectedExecutionException 异常。
(2)ThreadPoolExecutor.CallerRunsPolicy:该任务被线程池拒绝,由调用 execute方法的线程执行该任务。
(3)ThreadPoolExecutor.DiscardOldestPolicy : 抛弃队列最前面的任务,然后重新尝试执行任务。
(4)ThreadPoolExecutor.DiscardPolicy,丢弃任务,不过也不抛出异常。
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略。
4.线程队列
从有界无界上分
常见的有界队列为
ArrayBlockingQueue 基于数组实现的阻塞队列
LinkedBlockingQueue 其实也是有界队列,但是不设置大小时就是无界的。
ArrayBlockingQueue 与 LinkedBlockingQueue 对比一哈
ArrayBlockingQueue 实现简单,表现稳定,添加和删除使用同一个锁,通常性能不如后者
LinkedBlockingQueue 添加和删除两把锁是分开的,所以竞争会小一些
SynchronousQueue 比较奇葩,内部容量为零,适用于元素数量少的场景,尤其特别适合做交换数据用,内部使用 队列来实现公平性的调度,使用栈来实现非公平的调度,在Java6时替换了原来的锁逻辑,使用CAS代替了
上面三个队列他们也是存在共性的
put take 操作都是阻塞的
offer poll 操作不是阻塞的,offer 队列满了会返回false不会阻塞,poll 队列为空时会返回null不会阻塞
补充一点,并不是在所有场景下,非阻塞都是好的,阻塞代表着不占用CPU,在有些场景也是需要阻塞的,put take 存在必有其存在的必然性
常见的无界队列
ConcurrentLinkedQueue 无锁队列,底层使用CAS操作,通常具有较高吞吐量,但是具有读性能的不确定性,弱一致性——不存在如ArrayList等集合类的并发修改异常,通俗的说就是遍历时修改不会抛异常
PriorityBlockingQueue 具有优先级的阻塞队列
DelayedQueue 延时队列,使用场景
缓存:清掉缓存中超时的缓存数据
5.基本原理图