线程池
-
线程池的相关参数和作用
1.corePoolSize --核心线程数
2.maximumPoolSize --最大线程数
3.keepAliveTime --线程存活时间
4.TimeUnit --线程存活时间单位
5.BlockingQueue --线程队列
6.ThreadFactory --线程工厂
7.RejectedExecutionHandler --拒绝策略 -
线程池的相关工作流程
3.默认四种线程池的不足
1.newCachedThreadPool-创建可缓存的线程池
创建了一个无限大的线程池,并且所使用的队列没有容量.,拒绝策略是默认的丢弃任务并抛出异常
2.newFixedThreadPool-创建一个固定数量的线程池
创建一个核心线程数和最大线程数一样的线程池,所以他的线程数固定,队列采用无界队列,所以线程数量不会变化,队列会无限增长,造成oom.
3.newSingleThreadExecutor -创建一个单线程的线程池
创建一个核心线程数和最大线程数相等且都为1,线程队列采用无界队列的线程池,容易造成oom.
4.newScheduledThreadPool-定时线程池
创建一个定长线程池,支持定时及周期性任务执行。且队列也是无界队列
-
自定义线程池线程数的配置
3.1cpu密集型任务
一般加密,解密,压缩,计算等大量消耗cpu资源的任务,最佳线程数一般是cpu核心数的1~2倍,一般cpu密集型任务cpu都会满核运行,如果设置线程数过多会造成不必要的上下文切换,导致性能下降.
一般取cpu数+1.
3.2 Io型任务
一般数据库、文件的读写,网络通信等任务,不会特别消耗 CPU 资源,但是 IO 操作很耗时,总体会占用比较多的时间。对于这种任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费。而如果我们设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少,可以更好地利用资源。
一般配置核心线程数cpu数的2倍;
还有的是 num=cpu数/(1-阻塞系数)
w:等待时长
c:计算时长
阻 塞系数 = W / (W + C),即阻塞系数 = 阻塞时间 /(阻塞时间 + 计算时间) -
线程池的4种拒绝策略
4.1AbortPolicy
丢弃任务并抛出RejectedExecutionException异常。
这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
4.2DiscardPolicy
丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
4.3DiscardOldestPolicy
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
4.4 CallerRunsPolicy
CallerRunsPolicy:由调用线程处理该任务
5.shutdown和shutdownNow的区别
5.1shutdown()
shutdown(),它可以安全地关闭一个线程池,调用 shutdown() 方法之后线程池并不是立刻就被关闭,因为这时线程池中可能还有很多任务正在被执行,或是任务队列中有大量正在等待被执行的任务,调用 shutdown() 方法后线程池会在执行完正在执行的任务和队列中等待的任务后才彻底关闭。但这并不代表 shutdown() 操作是没有任何效果的,调用 shutdown() 方法后如果还有新的任务被提交,线程池则会根据拒绝策略直接拒绝后续新提交的任务。
5.2shutdownNow()
它与第一种 shutdown 方法不同之处在于名字中多了一个单词 Now,也就是表示立刻关闭的意思。在执行 shutdownNow 方法之后,首先会给所有线程池中的线程发送 interrupt 中断信号,尝试中断这些任务的执行,然后会将任务队列中正在等待的所有任务转移到一个 List 中并返回,我们可以根据返回的任务 List 来进行一些补救的操作.