线程池使用总结

线程池使用总结

Excutors工厂类

为了更好的控制更多的多线程,JDK提供返回一个固定数量的线程池。

方法:

  • newFixedThreadPool()方法:
    改方法返回一个固定数量的线程池,改方法的线程数,始终不变,当一个任务提交时,若线程池空闲,则立即执行,若没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行。
  • newSingleThreadPool()方法
    创建一个单例的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中
  • newCachedThreadPool()方法
    返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若有任务,则创建线程,若无任务则不创建线程,如果没有任务则线程会在60s后自动回收。
  • newScheduledThreadPool()方法:该方法返回一个ScheduledExcutorService对象,但该线程池可以指定线程的数量。

自定义线程池-ThreadPoolExecutors

  • 自定义线程池:ThreadPoolExecutor

线程池相关参数的配置

 ThreadPoolExecutor(int corePoolSize, //核心线程数,线程初始化就会被创建
                              int maximumPoolSize, //线程池最大线程数,无界队列时,不起作用
                              long keepAliveTime, //线程的存活时间
                              TimeUnit unit,      //时间的单位
                              BlockingQueue<Runnable> workQueue, //阻塞队列,使用无界队列时,拒绝策略无用
                              ThreadFactory threadFactory,//theadFactory 线程工厂,用于获取一个新的线程,然后把该线程投递到线程池里面去
                              RejectedExecutionHandler handler) //拒绝策略

1.当设置为有界队列时:

a 若有新的任务需要执行,如果线程实际数小于coreThread,则先创建线程

b.若大于coreSize,则会加入任务队列

c.若队列已满,则在不大于maxinumPoolsize的情况下创建新的线程

d.若线程数不大于maxinumPoolSize会执行拒绝策略

2.当为无界队列时

a.maximumPoolSize不起作用,也没有拒绝策略

b.若有新的任务需要执行,如果线程实际数小于coreThread,则先创建线程

c.若大于coreSize,则会加入任务队列,同时创建新的线程。

如何使用好线程池?

  • 线程个数大小的设置
  • 线程池相关参数的配置
  • 利用Hook嵌入你的行为
  • 线程池的关闭

线程池数量的设置

计算机密集型

应用需要非常多的CPU计算资源,避免过多的线程上下文切换

线程数 = CPU核数+1,已可以设置为CPU核数*2,还要看JDK的版本以及CPU配置(服务器的CPU有超线程)

IO密集型

Web应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束后,数据准备好后,线程才会继续执行。对于IO密集型的应用,我们可以多设置线程池中线程的数量,这样就能让等待IO的这段时间内,线程可以去做其它事,提高并发处理效率。线程上下文切换数有代价的

线程数 = CPU核数/(1-阻塞系数) 这个阻塞系数一般为0.8~0.9之间。

套用公司:对于双核CPU来说,比较理想的线程数就是20,当然这不是绝对的,需要根据实际情况以及实际业务来调整:final int poolSIze = (int)(copCore/(1-0.9))

线程池相关参数如何配置?

  • 高压线:
    • 我们使用线程池的时候都不要选择没有上限限制的配置项
  • 第一 我们不要去使用没有上限的线程池和设置无界队列!
  • 比如,newCachedThreadPool的设置与无界队列因为某些不可预期的情况,线程池会出现系统异常,导致线程暴增的情况或者任务队列或者任务队列不断膨胀,内存耗尽导致系统崩溃。我们建议自定义线程池来避免改问题,这是在使用线程池的首要选择。小心无大错,千万别过度自信。
  • 第二 合理设置线程数量、和线程空闲回收时间,根据具体的任务执行周期和时间去设定,避免频繁的回收和创建,虽然我们使用线程池的目的是为了提升系统性能和吞吐量,但是也要考虑下系统的稳定性,不然出现不可预期问题会很麻!
  • 第三,根据实际场景,选择适用于自己的拒绝策略。进行补偿,不要乱用JDK支持的自动补偿机制~尽量采用自定义的拒绝策略去进行兜底!

利用Hook去嵌入你的行为

  • 利用Hook,留下线程池执行的执行轨迹
  • ThreadPoolExcutor提供了protected类型可以被覆盖的钩子方法,运行用户在执行任务之前或执行之后做一些事情。我们可以通过它来实现比如初始化ThreadLocal、收集统计信息、如记录日志等操作,这类Hook如BeforeExcute和afterExecute。另外还有一个Hook可以用来在任务呗执行完的时候让用户插入逻辑,如reminated。
  • Hook方法执行失败,则内部的工作线程的执行将会失败或者中断。

关闭线程池

  • 内容当线程池不在被引用并且工作线程数为0的时候,线程池将被终止。我们也可以调用shutdown来手动终止线程池。如果我们忘记调用shutdown,为了让线程资源被释放,我们还可以使用keepAliveTime和allowShutdownHook方法,手工去调用线程池的关闭方法!
  • 最稳妥的方式是使用虚拟机Runtime.getRuntime().addShutdownHook方法,手动去调用线程池关闭方法。

猜你喜欢

转载自www.cnblogs.com/renqiqiang/p/10129352.html