How time flies! 好久没更了,估计有些小伙伴都忘了曾经关注过这个账号,突然看到陌生人,估计就把我删了^^,没关系,感谢你曾经关注过我这个默默无闻的做点小技术的coder。写东西确实挺耗时间的,反思之前的文章,体系和条理即突出重点做的稍差一点,未来会争取多读多写,还是一样,只写原创,内容不一定多有深度,甚至可能会有错,但是如果觉得对小伙伴有帮助,就会整理出来,不论短长。
闲言少叙,书接N个上回。
线程池是平时工作和面试的重点,最近要做些历史数据的割接:历史数据部署在本地系统中,要上传到云端Saas中去(中间有数据模型转换),数据割接完成后,会有关键指标一致性校验来兜底数据上传的完整性和准确性,但是整个过程需要监控,以便能更好的定位和发现数据上传的问题以及掌握数据上传的进度。
设计如下:在本地单个或整体上传任务完成后,都会向云端发起任务完成的通知,云端根据设置时间延迟查库和上传通知做比对,进行告警或结束提示,整个过程有统一trace的日志跟踪,以供数据不平时的分析,上传链路经过隔离的消息队列,增加限流(限流也是工作和面试的重点,可搜索令牌桶和漏桶等算法了解其区别)减少对正常业务的干扰。
由于数据传递有延迟,于是云端需要延迟根据上传通知做订单量匹配校验,另外上传细粒度任务可能非常多,为了减少对系统内存的压力,希望这个队列是有界的,搜了下concurrent包,没找到合适的轮子。
开始造轮子。
- 造个有界延迟队列 DelayQueue是无界延迟队列,借用其延迟功能,增加有界处理,当size达到capacity时,返回false。同步处理需要持有父类DelayQueue同一把lock,因此通过反射得到,详见构造函数。
public class BoundedDelayQueue<T extends DelayTask> extends DelayQueue<T> {
private final transient ReentrantLock lock;
private final int capacity;
BoundedDelayQueue(int capacity) {
try {
Field lockField = DelayQueue.class.getDeclaredField("lock");
lockField.setAccessible(true);
this.lock = (ReentrantLock)lockField.get(this);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error("Could not access lock field", e);
}
this.capacity = capacity;
}
@Override
public boolean offer(final T t) {
lock.lock();
try {
if (size() == capacity) {
return false;
}
return super.offer(t);
} finally {
lock.unlock();
}
}
}
- 以有界延迟队列手撸线程池 线程池继承Spring提供的ThreadPoolTaskExecutor,重写setMaxPoolSize、initialize、createQueue方法,为什么要重写这几个方法: 1、createQueue:根据传入的capacity,决定构建有界延迟队列还是无界; 2、setMaxPoolSize:如不重写,会导致部分任务无法得到延迟处理,稍后解读; 3、initialize:如不重写,会导致coreSize的任务无法得到延迟处理,稍后解读。 代码如下:
public class ThreadPoolDelayTaskExecutor extends ThreadPoolTaskExecutor {
@Override
public void setMaxPoolSize(int maxPoolSize) {
// 最大线程数和核心线程数保持一致
super.setMaxPoolSize(super.getCorePoolSize());
}
@Override
public void initialize() {
super.initialize();
// 预启动全部核心线程,以发挥延迟队列的作用
super.getThreadPoolExecutor().prestartAllCoreThreads();
}
@Override
public BlockingQueue<Runnable> createQueue(int queueCapacity) {
if (queueCapacity > 0) {
return new BoundedDelayQueue(queueCapacity);
} else {
// 无界延迟队列
return new DelayQueue();
}
}
}
线程池是如何运行的
见下面简陋的小鸡吃米图,上图为提交任务到线程池的完整执行流程,下图为线程池内线程的状态流程图。

演讲要像女孩子的裙子一样越短越好,码字亦如是。
