线程池原理解析

1、为什么要用线程池,线程池的作用(意义),如果使用线程池会有什么好处,说说你对线程池的了解?

创建和销毁线程的代价是很大的,线程不像创建普通object对象那样,在堆中分配点内存就好了的,在new Thread的时候,是先分配内存,但是并没有创建线程,当调用线程的start方法的时候,会通过cpu指令创建一个线程。所以,频繁的创建线程、销毁线程的代价是很大的,会导致系统性能下降。还有,如果频繁创建线程,不加以管理,会导致系统线程过多,影响系统运行。

如果用线程池,指定线程数量,线程池会帮助我们管理线程,我们使用线程可以从线程池获取一个线程,线程干完活,在放回池子里,不会销毁。而且有线程池管理线程,能有效控制线程的数量,不会导致系统创建过多的线程。

  • 创建和销毁线程的代价是很大
  • 能有效控制线程的数量,不会导致系统创建过多的线程。

2、线程池的内部结构(组成)?

Java线程池包含4个部分,线程池的工作原理,是生产者消费者模式的

(1)线程池管理器(ThreadPool):就是负责创建和销毁线程池的

(2)工作线程(PoolWorker):就是线程池中的一个线程

(3)工作任务(Task):这个就是线程池里的某个线程需要执行的业务代码,这个是你自己编写的业务逻辑

(4)任务队列(TaskQueue):这个是扔到线程池里的任务需要进行排队,要进任务队列

3、常用的线程池有哪些?

(1)SingleThreadExecutor(单线程线程池,很少用 -> 自己做一个内存队列 -> 启动后台线程去消费)

里面就一个线程,慢慢消费

(2)FixedThreadExecutor(固定数量线程池):比如说,线程池里面固定就100个线程,超过这个线程数就到队列里面去排队等待

(3)CachedThreadExecutor(自动回收空闲线程,根据需要自动新增线程,传说中的无界线程池???真的无限界吗?):无论有多少任务,根据你的需要,无限制的创建任意多的线程,在最短的时间内来满足你,但是高峰过去之后,如果有大量的线程处于空闲状态,没有活儿可以干,等待60s之后空闲的线程就被销毁了

(4)ScheduledThreadExecutor(线程数量无限制,支持定时调度执行某个线程):提交一个任务,对于这个任务不是立马执行的,是可以设定一个定时调度的逻辑,比如说每隔60s执行一次,这个一般不用,一般来说就用spring schedule的支持

一般其实最常用的是 FixedThreadExecutor 和 CachedThreadExecutor

4、线程池有哪些常用的API?

(1)Executor:代表线程池的接口,有个execute()方法,扔进去一个Runnable类型对象,就可以分配一个线程给你执行

(2)ExecutorService:这是Executor的子接口,相当于是一个线程池的接口,有销毁线程池等方法

-> ExecutorService就代表了一个线程池管理器,会负责管理线程池 ->负责 线程的创建和销毁 -> 队列排队

(3)Executors:线程池的辅助工具类,辅助入口类,可以通过Executors来快捷的创建你需要的线程池。

创建线程池的入口类,包含newSingleThreadExecutor()、newCachedThreadPool()、newScheduleThreadPool()、newFixedThreadPool(),这些方法,就是可以让你创建不同的线程池出来

(4)ThreadPoolExecutor:这是ExecutorService的实现类,这才是正儿八经代表一个线程池的类,一般在Executors里创建线程池的时候,内部都是直接创建一个ThreadPoolExecutor的实例对象返回的,然后同时给设置了各种默认参数。

如果我们要创建一个线程池,两种方式,要么就是Executors.newXX()方法,快捷的创建一个线程池出来,线程池的所有参数设置都采取默认的方式;要么是自己手动构建一个ThreadPoolExecutor的一个对象,所有的线程池的参数,都可以自己手动来调整和设置

5、用executor怎么创建线程池,怎么用的?

源码:

Executors使用:

6、说一下线程池配置的几个参数?

3.3 线程池的构造参数和真正的工作原理

ThreadPoolExecutor就是线程池,那么这个类的构造函数的所有入参,就是你可以设置的参数,我们来解释一下这些参数吧

  • corePoolSize:线程池里的核心线程数量

  • maximumPoolSize:线程池里允许有的最大线程数量

  • keepAliveTime:如果线程数量大于corePoolSize的时候,多出来的线程会等待指定的时间之后就被释放掉,这个就是用来设置空闲线程等待时间的

  • unit:这个是上面那个keepAliveTime的单位

  • workQueue:这个是说,通过ThreadPoolExecutor.execute()方法扔进来的Runnable工作任务,如果所有线程都繁忙,会进入一个队列里面去排队,这就是那个队列

  • threadFactory:如果需要创建新的线程放入线程池的时候,就是通过这个线程工厂来创建的

  • handler:假如说上面那个workQueue是有固定大小的,如果往队列里扔的任务数量超过了队列大小,咋办?就用这个handler来处理,
    • AbortPolicy、
    • DiscardPolicy、
    • DiscardOldestPolicy,
    • 默认:如果说线程都繁忙,队列还满了,此时就会报错,RejectException

7、结合线程池的参数,说说线程池的工作原理?线程池启动线程的原理(说说线程池的底层原理)?

上来就画图

然后假设参数是这些

注意:队列大小可以自己设置,如果不设置,那大小就是(核心线程数+最大线程数)

总结一下下面这些话:总的来说,就是先创建线程到核心线程数,然后再把队列塞满(放四个还是放2个(队列+核心线程数)??JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue-CSDN博客),如果队列满了,再来任务,就再创建线程(最多可以创建,最大线程数 - 核心线程数),这时候创建的几个线程如果长时间不工作是要回收的。如果线程总数达到了最大线程数,而且,队列满了,那就走handler的策略,默认抛出rejectExectorException

(1)一开始线程池里的线程是空的,一个都没有。

有一个变量维护的是当前线程数量,这个变量是poolSize,poolSize = 0,

如果当前线程的数量小于corePoolSize(2),poolSize < corePoolSize,

那么来了一个任务优先创建线程,直到线程池里的线程数量跟corePoolSize一样;

poolSize = 1,poolSize < corePoolSize(2),又创建一个线程来处理这个任务;poolSize = 2

(2)如果当前线程池的线程数量(poolSize = 2)大于等于corePoolSize(2)的时候,而且任务队列没满(最大大小是4,但是当前元素数量是0),那么就扔到任务队列里去

(3)如果当前线程池的线程数量大于等于corePoolSize的时候,而且任务队列满了(不一定是4,可以自己设置)(最大大小是4,当前已经放了4个元素了,已经满了),那么如果当前线程数量小于最大线程数(poolSize = 2,maimumPoolSize = 4,poolSize < maximumPoolSize),就继续创建线程;poolSize = 3,提交了一个任务,poolSize >= corePoolSize,任务队列满,poolSize < maximumPoolSize,再次创建一个任务

(4)如果此时poolSize >= corePoolSize,任务队列满,poolSize == maximumPoolSize,此时再次提交一个任务,当前线程数已经达到了最大线程数了,那么就使用handler来处理,默认是抛出异常,ThreadPoolRejectExeception

(5)此时线程池里有4个线程,都处于空闲状态,corePoolSize指定的是就2个线程就可以了,但是此时超过了corePoolSize 2个线程,所以如果那超出的2个线程空闲时间超过了60s,然后线程池就会将超出的2个线程给回收掉

如何设置池的这些参数?先来看看创建线程池的默认代码

其实上面都说过了,啥时候会创建新线程?其实就是线程数没到corePoolSize的时候,会创建线程;接着就是任务队列满了,但是线程数小于maximumPoolSize的时候,也会创建线程;创建的时候通过threadFactory来创建即可

7、线程池队列满的时候怎么处理(FixedThreadPool满了之后会怎么办)?

如上个问题,如果队列满了,就会创建新的线程,如果最大线程数比核心线程数大的话,然后等线程池中的线程数量达到最大数量了,并且队列也满了,那就抛异常(也可以选择别的策略)RejectExectingException

8、FixedThreadPool 的工作原理?

源码:

(1)核心线程数,等于最大线程数

(2)超时时间(根本不会超时,因为都是核心线程,所以就写个0L)

(3)创建了一个 LinkedBlockingQueue 无界队列 ,只要有任务就会往队列里加,知道内存溢出

9、CachedThreadPool的工作原理

源码:

(1)corePoolSize = 0,maximumPoolSize = 最大值(无限大),keepAliveTime = 60s,workQueue=SynchronousQueue

(2)SynchronousQueue(实际上没有存储数据的空闲,是用来做多线程通信之间的协调作用的),一开始提交一个任务过来,要求线程池里必须有一个线程对应可以处理这个任务,但是此时一个线程都没有,poolSize >= corePoolSize , workQueue已经满了,poolSize < maximumPoolSize(最大值),直接就会创建一个新的线程来处理这个任务

(3)如果短期内有大量的任务都涌进来,实际上是走一个直接提交的思路,对每个任务,如果没法找到一个空闲的线程来处理它,那么就会立即创建一个新的线程出来,来处理这个新提交的任务

(4)短时间内,如果大量的任务涌入,可能会导致瞬间创建出来几百个线程,几千个线程,是不固定的

(5)但是当这些线程工作完一段时间之后,就会处于空闲状态,就会看超过60s的空闲,就会直接将空闲的线程给释放掉

10、FixedThreadPool 和 CachcedThreadPool 的区别

  把上面那个说一下,然后加上下面这些

(1)FixedThreadPool:线程数量是固定的,如果所有线程都繁忙,后续任务全部在一个无界队列里面排队,无限任务来排队,直到内存溢出

(2)CachcedThreadPool:线程数量是不固定的,如果一下子涌入大量任务,没有空闲线程,那么就创建新的线程来处理;如果有空闲线程,就是一个任务配对一个空闲线程来处理;如果线程空闲时间超过60s,就给回收掉空闲线程,线程大小无限大有点不靠谱,容易挂。4核8G的虚拟机,100个线程就差不多了,cpu可能就快达到了70%,80%

11、不同线程池的使用场景是什么?

FixedThreadPool:比较适用于什么场景呢?负载比较重,而且负载比较稳定的这么一个场景,我给大家来举个例子,我们之前线上有一套系统,负载比较重,后台系统,每分钟要执行几百个复杂的大SQL,就是用FixedThreadPool是最合适的。

因为负载稳定,所以一般来说,不会出现说突然瞬间涌入大量的请求,100个线程处理不过来,然后就直接无限制的排队,然后oom内存溢出,死了

CachedThreadPool:负载很稳定的场景,用CachedThreadPool就浪费了;每天大部分时候可能就是负载很低的,CachedThreadPool,用少量的线程就可以满足低负载,不会给系统引入太大的压力;但是每天如果有少量的高峰期,比如说中午或者是晚上,高峰期可能需要一下子搞几百个线程出来,那么CachedThreadPool就可以满足这个场景;高峰期应付过去之后,线程如果处于空闲状态超过60s,自动回收空闲线程,避免给系统带来过大的负载

12、说一下该如何设置线程池的参数,设置多大合适?

(处理时间,如:100ms)* 任务总数(100) = 10(100个任务要10s)

如果想在1s内处理完 100 个任务,那就要有10个线程,如果想在2s内处理完,那就设置5个线程

  • 核心线程数可以考虑 一天请求量的平均值,算出来一个
  • 最大线程数,可以根据高峰时段的请求数,算出来一个
  • 超时间是一般设置60s左右就行
  • 队列的话,可以自己设置个有界队列ArrayBlockingQueue,如果使用无界队列容易内存溢出,配合自己写的handler策略
  • handler的话,可以自己实现这个接口,写一个自己类似降级的逻辑,传给线程池,比如说如果发生意外,队列满了,那就走handler里的逻辑,离线存储,(然后自己写个线程,扫描mysql的handler的那个表,执行)。

4核8G的虚拟机,100个线程就差不多了,cpu可能就快达到了70%,80%

13、线程池被关闭的方式有哪几种?

  • shutdown():调用之后不允许提交新的任务了,所有调用之前提交的任务都会执行,等所有任务执行完了,才会真正关闭线程池,这就是优雅的关闭方式

  • shutdownNow():返回还没执行的task列表,然后不让等待的task执行,尝试停止正在执行的task,非优雅关闭,强制关闭

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值