线程池、死锁排查和Disruptor

69 阅读3分钟

线程池

  1. acc:获取调用上下文
  2. corePoolSize:核心线程数量,可以类比正式员工数量,常驻线程数量。maximumPoolSize:最大的线程数量,公司最多雇佣员工数量。常驻+临时线程数量。
  3. workQueue:多余任务等待队列,再多的人都处理不过来了,需要等着,在这个地方等。
  4. keepAliveTime:非核心线程空闲时间,就是外包人员等了多久,如果还没有活干,解雇了
  5. threadFactory: 创建线程的工厂,在这个地方可以统一处理创建的线程的属性。每个公司对员工的要求不一样,恩,在这里设置员工的属性
  6. handler:线程池拒绝策略,什么意思呢?就是当任务实在是太多,人也不够,需求池也排满了,还有任务咋办?默认是不处理,抛出异常告诉任务提交者,我这忙不过来了。

拒绝策略

  1. AbortPolicy-这是默认的拒绝策略,当线程池无法接受新任务时,会抛出RejectedExecutionException异常。这意味着新任务会被立即拒绝,不会加入到任务队列中,也不会执行。通常情况下都是使用这种拒绝策略。
  2. DiscardPolicy-这个策略在任务队列已满时,会丢弃新的任务而且不会抛出异常。新任务提交后会被默默地丢弃,不会有任何提示或执行。这个策略一般用于日志记录、统计等不是非常关键的任务。
  3. DiscardOldestPolicy-这个策略也会丢弃任务,但它会先尝试将任务队列中最早的任务删除,然后再尝试提交新任务。如果任务队列已满,且线程池中的线程都在工作,可能会导致一些任务被丢弃。这个策略对于一实时性要求较高的场景比较合适
  4. CallerRunsPolicy-这个策略将任务回退给调用线程,而不会抛出异常。调用线程会尝试执行任务。这个策略可以降低任务提交速度,适用于任务提交者能够承受任务执行的压力,但希望有一种缓冲机制的情况。 基本上使用AbortPolicy,使用DiscardPolicy不会抛弃,导致丢失不知道,AbortPolicy太长了会抛出异常,但是这个时候基本不是需要拓展长度就是需要优化了,所以基本默认就可以了。

死锁排查

还是arthas。
thread -b:查询所有被阻塞的线程
thread -n 3:查询3个最繁忙的线程:显示ID,CPU使用率,deltatime采样间隔时间内线程的增量 CPU 时间,time 线程运行总 CPU 时间。
arthas.aliyun.com/doc/thread.… image.png

Disruptor

tech.meituan.com/2016/11/18/…
核心理解

1. 为什么不用ArrayBlockingQueue?

(1)加锁

(2)伪共享

CPU是一次缓存64个字节的,比如long是8字节。则一次性缓存它和边上的8个long。
ArrayBlockingQueue有三个成员变量: - takeIndex:需要被取走的元素下标 - putIndex:可被元素插入的位置的下标 - count:队列中元素的数量
这导致了读取一个的时候其他两个很可能被缓存了,但是修改后不会修改缓存,导致每次都需要去主存里面重新读取数据

伪共享一般的解决方案是,增大数组元素的间隔使得由不同线程存取的元素位于不同的缓存行上,以空间换时间。

Disruptor因为是数组,放的位置是通过位运算的,就像bitmap,因此不是连续的。 在jdk1.8中,有专门的注解@Contended

2. Disruptor核心

我认为主要是两个队列长度:
Ring Buffer :已经分配了可读和预分配位置
available Buffer:可读和实际写好的
下图:下次开始写的时候从12开始分配。读的时候因为7还没写好,所以只能读到6 image.png