说说线程池核心参数和执行原理。
线程池(以 Java 的 ThreadPoolExecutor 为例)创建时主要围绕 7 个核心参数展开,理解了这 7 个参数,基本就掌握了线程池的命脉。
- corePoolSize(核心线程数):线程池长期保有的核心线程数量,即使空闲也不会回收(除非设置 allowCoreThreadTimeOut)。
- maximumPoolSize(最大线程数):线程池允许创建的最大线程数,等于核心线程数 + 非核心线程数。
- keepAliveTime + unit(空闲存活时间及单位):非核心线程空闲超过这个时间就会被回收,释放资源。
- workQueue(工作队列):核心线程都在忙时,新提交的任务会先放进这个阻塞队列等待。常见有 SynchronousQueue(不缓存,直接交给线程)、LinkedBlockingQueue(无界,容易堆积)、ArrayBlockingQueue(有界)、PriorityBlockingQueue(优先级队列,无界,不是 FIFO)。
- threadFactory(线程工厂):用于创建线程,可以统一设置线程名、优先级、是否守护线程,方便排查问题。
- handler(拒绝策略):当队列满了且线程数达到最大线程数,再提交任务就会触发拒绝策略。内置四种:AbortPolicy(中止策略,抛异常后丢弃,默认)、CallerRunsPolicy(调用者运行策略,让提交任务的线程自己执行)、DiscardPolicy(丢弃策略,直接丢弃)、DiscardOldestPolicy(丢弃最老任务策略,丢弃队列最老的任务后重新尝试提交当前新任务)。
执行原理(任务提交后的流转)是关键,记住这条主线:
- 第一步:当前线程数 < corePoolSize,直接创建核心线程执行任务,不管有没有空闲线程。
- 第二步:线程数已经 >= corePoolSize,任务被塞进 workQueue 排队。
- 第三步:队列也满了,且线程数 < maximumPoolSize,创建非核心线程立即执行该任务。
- 第四步:队列满了且线程数已经达到 maximumPoolSize,执行拒绝策略。
所以任务处理优先级是:核心线程 > 工作队列 > 非核心线程 > 拒绝策略,而不是简单地”先加线程”。这也解释了为什么用无界队列(如 LinkedBlockingQueue)时 maximumPoolSize 形同虚设——队列永远塞不满,非核心线程永远不会被创建,最终导致任务无限堆积、内存飙升。实际配置时核心线程数和队列容量要一起权衡,配合有界队列 + 合理的拒绝策略才稳妥。
另外DiscardOldestPolicy 搭配 PriorityBlockingQueue 会有什么问题? PriorityBlockingQueue 不是 FIFO,它会按优先级排序,所谓 “最老” 不是等待时间,而是队列头部元素,会错误丢掉优先级最高任务,禁止两者搭配使用。
连接查询是mysql多表查询的核心,我主要从逻辑和执行两个层面连接它。
从逻辑层面来说,连接查询的核心是对多表之间的数据关联,主要分为内连接和外连接。内连接查询的是数据的交集。外连接包括左连接、右连接和全外连接,mysql不支持全外连接。左、右连接的逻辑是保留对应的表的全量数据,对于另一个表中没有匹配的记录用NULL填充。实际开发中通常都统一使用左连接代替右连接来保证代码可读性。
从执行层面来说,连接查询的关键在于驱动表的选择和索引的使用。内连接查询时mysql优化器会根据统计信息自动选择小表作为驱动表。外连接查询驱动表固定为主表(即左连接查询驱动表就是左表),所以为了发挥B+树的优势,避免被驱动表发生全表扫描,被驱动表一定要对关联字段加索引。
另外,使用外连接时要特别注意on和where字句的区别,ON 是在生成临时表时使用的条件(联表前筛选),而 WHERE 是在临时表生成之后对结果进行过滤的条件(联表后筛选)。如果对从表的过滤条件写在where中,会导致外连接退化成内连接。所以如果想保留主表的所有记录,那么针对从表的筛选条件必须写在 ON 里。如果想过滤最终的结果集(包括过滤掉没匹配上的主表记录),才把条件写在 WHERE 里。
第一条说说




