为什么使用线程池
比如说下面这张图,是一个早期的 web 服务器模型。对于每一个请求,服务器都会新建一个线程去处理。这样在并发很小的场景似乎问题还不大,但是访问量稍微上去一点就很难扛得住了。所以我们需要使用线程池来合理地处理这些任务。
image.png
线程池注意事项
死锁
常见死锁场景,例如线程1拿到A资源等待B资源,线程2拿到B资源等待A资源,这样子互相等待陷入死循环,于是就死锁了。使用线程池的时候也比较容易遇到死锁,例如正在执行的线程依赖正在排队的线程占有的资源,这样子就会死锁了。
资源枯竭
常见的场景是一个调用链路耗时比较长的任务很久都没运行完,如果经常这样子就会长时间占用系统的资源而不能及时地区执行其他任务。线程池有个超时时间参数,我们可以根据具体的业务和经验判断合理地设置这一个参数。例如20s以内一个请求还没处理完可以强制回收线程资源。
并发问题
主要使用乐观锁来解决并发情况下的资源抢占问题,悲观锁性能差不太考虑。
线程泄漏
线程泄漏就是指一个特定容量的线程池,里面的线程长时间没执行完任务而无法去执行工作队列里的其他任务,这样子相当于线程池的容量被减少了,也称线程泄漏。
请求过载
请求过载是指某个时间段可能突然有大量的请求过来一下子把线程池占满,这样子服务就雪崩了,我们需要一些服务熔断的机制来保障服务不崩溃。
线程池
image.png以上是大多数线程池使用的模型,包括jdk,Spring和tomcat等web容器。
- 调用ThreadPoolExecutor的execute提交线程,首先检查CorePool,如果CorePool内的线程小于CorePoolSize,新创建线程执行任务。
- 如果当前CorePool内的线程大于等于CorePoolSize,那么将线程加入到BlockingQueue。
- 如果不能加入BlockingQueue,在小于MaxPoolSize的情况下创建线程执行任务。
- 如果线程数大于等于MaxPoolSize,那么执行拒绝策略。(可自定义)
网友评论