Java线程池笔记——合理的配置线程池

要想合理的配置线程池,就必须首先分任务特性,可以从以下几个角度来分析。

  • 任务的性质:CPU密集型任务,IO密集型任务和混合型任务。

  • 任务的优先级:高中低。

  • 任务的执行时间:长,中,短。

  • 任务的依赖性:是否依赖其他系统资源,如数据库连接。

性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应该配置尽可能小的线程,如配置N(CPU)+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如N*2。
混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果两个任务执行的时间差别太大,则没有必要进行分解。
可以功能通过Runtime.getRuntime().availableProcessors()获取当前设备的CPU数目。
优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。他可以让优先级搞的任务先去执行(如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行)。
执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间段的任务先执行。
依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。
PS:建议使用有界队列
有界队列能增加系统的稳定性和预警能力,可以根据需要设置大一点儿,比如几千。试想,假设任务执行的异常缓慢,则任务队列里面,随着时间,就会越来越多的任务积累。如果此时使用的是无界队列,那么,可能会撑爆内存,到时候,不仅线程所在的应用不能使用,本机服务器也会受到严重影响,如果本机上还部署这其他服务,就会波及到其他应用也不能使用。
之前遇到过一个惨痛的例子,就是,存入HBase数据时候,使用了个CachedThreadPool,来执行存入数据的消费工作。但是由于上游Kafka数据每秒上来过多,而网络IO存入又没有那么快,就会不断创建线程,一段时间之后,就会由于线程过多,造成应用卡死了。附带服务器也运行缓慢,ssh都困难。
遇到这个血泪教训之后,改用了固定个数的线程池。

你可能感兴趣的:(并发编程)