计算机系统应用教程网站

网站首页 > 技术文章 正文

JAVA线程池之一核心参数详解 java线程池参数设置

btikc 2024-10-22 10:32:02 技术文章 6 ℃ 0 评论

本章主要内容来源于

Java Threads(3rd Edition)(OReilly) author:Scott Oaks, Henry Wong

Java concurrent in practice author:Brian Goetz / Tim Peierls / Joshua Bloch / Joseph Bowbeer / David Holmes / Doug Lea

还有很多地方需要深入研究以及没有理解到位的地方,但是我会尽最大的努力做到知识全面化,为大家带来更详细,更深入的知识,如果有那些地方您觉得不对,希望可以在评论区指出,小编会感激不尽!!!

1. 线程池有那些好处

对于开发者来说,如果没有线程池我们需要创建大量的Thread,不论是开发者还是应用程序这都不是一个好的现象。而有了线程池我们只需要提交Runnable(Callable)这样的对象到线程池中,省去了对线程生命周期则的管理,并且实现了线程的复用。线程池中还维护了一个BlockingQueue,这样使得消费者和提供者可以分离,使开发者只需要关注业务逻辑即可,这样使得代码更加内聚,更加便于维护,当然这也只是解决了大部分问题,有时候线程池中提交任务的速度快于消费者处理业务的话,我们也需要拒绝新的任务到线程池,直到线程池中有空闲的位置,也有一些任务并不适用于线程池比如大文件的读取。

2. 线程池核心参数设置以及详解

接下来我们讨论下线程池的构造,线程池有很多构造函数,下面的构造函数是参数最齐的,已经覆盖所有构造函数的参数

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler);

}

首先说下各个参数含义:

coreSize 核心线程数

maximumPoolSize 最大线程数

keepAliveTime 线程存活时间

Unit 线程存活时间的时间单位比如秒,毫秒等

workQueue blockQueue是专门为高并发设计的容器

threadFactory 可以更线程默认的名字,状态,线程组,优先级策略

Handler 拒绝策略

总的来说线程池分为四个部分,分别是控制线程数量的coreSize,maximumPoolSize参数;控制队列的workQueue;控制线程存活时间keepAliveTime,unit和控制线程信息的threadFactory;最后是线程的拒绝策略handler,可以说线程池主要就是这四部分,除了构造函数之外ThreadPoolExecutor也提供了方法对核心参数进行修改,为了更进一步讨论,请看以下代码,在线程池中核心线程数设置为5,最大线程数为10,提交了110个任务


当我们创建线程池的时候,还没有提交任务到线程池,其实线程池中的是没有线程的,当提交了第一个任务,线程池则会创建一个线程,直到达到了核心线程数,这个时候如果核心线程数有空闲的新提交的任务会交给空闲的线程,如果没有空闲的线程则会提交到工作队列(BlockingQueue)中,如果Blockingqueue 如果已经满了,则创建新的线程,直到达到最大线程数,如果这个时候消费者处理速度还没有追上提供者,则会调用拒绝策略,拒绝新任务提交。所以本例中线程池一共可以容纳110个任务,工作队列容纳100个任务,核心线程处理5个,工作队列达到边界,创建新的线程到最大线程数.所以可能容乃110个任务,如果提交到第111个,线程池则会拒绝任务的提交。


还有就是工作队列(BlockingQueue)页影响着线程池的工作方式,上面例子中我们使用的是有界的LinkedBlockingQueue,java5开始提供了一系列的Blockingqueue有ArrayBlockingQueue,SynchronousQueue,LinkedBlockingQueue,PriorityBlockingQueue,DelayQueue等

SynchronousQueue:实际上并不是真正意义上的队列,SynchronousQueue更多的是在线程之间共享任务,工作队列的大小实际上是0,我们可以看到,当提交的任务超过11个时,由于消费者的处理能力追不上提供者,所以线程池拒绝了新的任务,因为使用SynchronousQueue处理任务的时候SynchronousQueue并不会排队,要么立即处理要么立即失败。



DelayQueue:延迟队列,实际上这个BlockingQueue并不适用于ThreadPoolExecutor,因为DelayQueue是延迟队列,而另一个线程池正是使用DelayQueue,才实现了定时调用的功能,这个线程池就是ScheduledThreadPoolExecutor


LinkedBlockingQueue:无边界队列:

注意LinkedBlockingQueue没有设置边界就是无限容量。在这种情况下,总是将任务添加到队列中成功,这意味着线程池不会创建超过M的线程和从不拒绝任务。

LinkedBlockingQueue(100),ArrayBlockingQueue,PriorityBlockingQueue有界队列:

假设队列P的界限。当任务被添加到池中时,它将创建线程,直到达到M个线程。此时,它开始将任务排队,直到等待的任务数达到P。随着添加更多任务,线程池开始添加线程,直到达到N个线程。如果N个线程处于活动状态,而P个任务处于队列中,则其他任务被拒绝。

PriorityBlockingQueue 是带有优先级的有界队列,默认大小为11个


那么我们应该如何设置这些参数呢?

如果线程数量过大则会在相对较小的CPU和内存中发生竞争,如果过小则会由很多空闲的CPU,导致吞吐量的不足。所以我们需要分析环境中有多少个CPU,内存是多大,是计算密集型的操作还是IO密集型的操作。对于计算密集型的任务,当前线程数量为CPU数量+1为最优。对于IO密集型或者有阻塞的任务,由于线程并不会一致执行,因此线程池应该过大,推荐的计算公式为

CPU利用率=CPU数量x目标CPU利用率x(1+(等待时长/计算时长))

CPU数量可以通过 Runtime.getRuntime().availableProcessors()获取


Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表