最新消息:

Java线程池的管理

java 老鹰 933浏览 0评论

线程池的大小是指:线程池中工作的线程的数量。

线程的数量过多,会导致消耗过多的资源,增加上下文的切换(线程的挂起,当前线程在cpu的缓存的数据的保存,以及线程栈内数据的恢复等)。

线程的数量太小,会导致无法充分利用CPU资源,使任务处理的吞吐率过低。

因此,合理设置线程数量也变得非常重要。

设置线程数量取决于当前线程池所要处理的任务的特性系统资源状况以及任务所使用的稀缺资源状况等因素。

 

通常,线程池的大小不是硬编码在代码中的,而是可配置的,或者是动态计算出来的。

java.lang.Runtime类的availableProcessors方法获取JVM宿主机CPU个数。

Ncpu 标示系统的CPU个数。

系统资源状况是指:系统CPU个数,JVM堆内存的大小。

任务的特性是指:当前任务是CPU密集型、IO密集型,还是混合型(同时包含较多计算和IO操作)。

A)CPU密集型任务:相应的线程池的大小可以配置成Ncpu + 1 (多1考虑到的是可执行线程因为某种原因,如缺页中断(Page Fault)而出现的等待。此时,一个额外的线程可以继续使用CPU时间片).

B)对于IO密集型的任务:一般最大可设置成2*Ncpu个线程。IO操作会引起上下文切换,顾可将线程池的核心线程池大小设置为1,最大设置为2*Ncpu,这样,如果线程池只需要一个工作线程就可以轻松处理提交给其的任务,如果一个工作者线程无法满足任务处理的需要,那么ThreadPooExecutor会逐渐的增加工作者线程的数量,直到到达Ncpu*2.

例如如下代码01

public void iOThreadPool(){

  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(

       //核心线程池大小为1
1,
//最大线程大小为 2*Ncpu
Runtime.getRuntime().availableProcessors() * 2,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(200));

  threadPool.submit(new IOIntensiveTask());

private static class IOIntensiveTask implements Runnable {

@Override
public void run() {
//执行大量的IO操作
}

}

实际上,商用软件规定某个软件在其运行过程中CPU使用率不得超过某个限定值(如75%)。

因此,要进一步“精确”地设置线程池的大小,我们可能需要考虑目标CPU使用率,即我们期望软件运行过程中会保持多少平均CPU使用率。

下面给出一个计算线程合理大小的公式,如下所示:

S = Ncpu * Ucpu * (1 + WT/ST

S为线程池合理的大小,Ncpu为CPU的个数,Ucpu为目标CPU使用率,

WT为任务执行线程进行等待的时间,

ST为任务执行线程使用CPU计算时间。

WT和ST可借助jvisualvm计算出相应值。

另外,如果是稀缺资源:如数据库链接,也会对线程池的合理大小产生影响。

总而言之,线程池的合理大小不是一件能精确做到的事情。重要的是,线程池大小要可以配置,并且其配置值要考虑到设置线程池的大小提供了参考,使得线程池大小调校有了一个起点和依据。

 线程池监控是指:对线程池的大小,工作队列的容量,线程空闲时间限制等的监控。

线程池ThreadPoolExecutor提供了如下的监控方法:

1)getPoolSize() :获取当前线程池的大小;

2)getQueue() : 获取工作队列实例,进而获取工作队列的当前大小;

3)getLargestPoolSize() : 获取工作者线程数曾经达到的最大数,该数值有助于确认线程池的最大大小设置是否合理;

4)getActiveCount() : 获取当前线程池正在执行任务的工作者线程数(近似值);

5)getTaskCount() : 获取当前线程池所接收到的任务数(近似值);

6)getCompletedTaskCount() : 获取线程池到目前为止已经处理完毕的任务数(近似值)。

线程泄漏是指线程池中的工作者线程意外中止。

如果线程持续存在线程泄漏,那么线程池中的工作者线程会越来越少,最终使得线程池无法处理提交其的任务。

线程泄漏通常是由于线程对象的run方法中异常处理没有捕捉RuntimeException和Error导致run方法意外返回,使得相应线程提前终止。

 

可靠性与线程池饱和处理策略是指有界队列作为工作队列时,当线程池工作队列满,并且工作者线程数量已经达到最大工作者线程数时(线程池最大大小),此时,新提交的业务,默认情况下就会被拒绝。

代码01中,new ThreadPoolExecutor() 方法中,还有一个参数,是在发生上面描述的情况时,给的一个处理方式,参数是需要实现接口RejectedExecutionHandler。

在Jdk中给了四个实现,分别是:

1)ThreadPoolExecutor.AbortPolicy class,超出最大线程后,将抛出异常;

2)ThreadPoolExecutor.DiscardPolicy class, 超出最大线程后,将不做任何处理;

3)ThreadPoolExecutor.DiscardOldestPolicy class 超出最大线程后,将缓冲区中最旧的任务放弃,然后,重新尝试接纳被拒绝的任务;

4)ThreadPoolExecutor.CallerRunsPolicy class 超出最大线程后,在客户端线程中执行被拒绝的任务

转载请注明:冯英胜的博客 » Java线程池的管理

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址