网站基础内容,注册百度网站怎么弄,广州海珠网络营销外包,济南住建局官方网站文章目录 一. 按照任务类型对线程池进行分类1. IO密集型任务的线程数2. CPU密集型任务的线程数3. 混合型任务的线程数 二. 线程数越多越好吗三. Redis 单线程的高效性 使用线程池的好处主要有以下三点#xff1a; 降低资源消耗#xff1a;线程是稀缺资源#xff0c;如果无限… 文章目录 一. 按照任务类型对线程池进行分类1. IO密集型任务的线程数2. CPU密集型任务的线程数3. 混合型任务的线程数 二. 线程数越多越好吗三. Redis 单线程的高效性 使用线程池的好处主要有以下三点 降低资源消耗线程是稀缺资源如果无限制地创建不仅会消耗系统资源还会降低系统的稳定性通过重复利用已创建的线程可以降低线程创建和销毁造成的消耗。提高响应速度当任务到达时可以不需要等待线程创建就能立即执行。提高线程的可管理性线程池提供了一种限制、管理资源的策略维护一些基本的线程统计信息如已完成任务的数量等。通过线程池可以对线程资源进行统一的分配、监控和调优。 虽然使用线程池的好处很多但是如果其线程数配置得不合理不仅可能达不到预期效果反而可能降低应用的性能。接下来按照不同的任务类型来配置线程池。 一. 按照任务类型对线程池进行分类
使用标准构造器ThreadPoolExecutor创建线程池时会涉及线程数的配置而线程数的配置与异步任务类型是分不开的。这里将线程池的异步任务大致分为以下三类 IO密集型任务此类任务主要是执行IO操作。由于执行IO操作的时间较长导致CPU的利用率不高这类任务CPU常处于空闲状态。Netty的IO读写操作为此类任务的典型例子。CPU密集型任务此类任务主要是执行计算任务。由于响应时间很快CPU一直在运行这种任务CPU的利用率很高。混合型任务此类任务既要执行逻辑计算又要进行IO操作如RPC调用、数据库访问。 相对来说由于执行IO操作的耗时较长一次网络往返往往在数百毫秒级别这类任务的CPU利用率也不是太高。Web服务器的HTTP请求处理操作为此类任务的典型例子。一般情况下针对以上不同类型的异步任务需要创建不同类型的线程池并进行针对性的参数配置。 1. IO密集型任务的线程数
由于IO密集型任务的CPU使用率较低导致线程空余时间很多因此通常需要开CPU核心数两倍的线程。当IO线程空闲时可以启用其他线程继续使用CPU以提高CPU的使用率。
Slf4j
//懒汉式单例创建线程池用于IO密集型任务
public class IoIntenseTargetThreadPoolLazyHolder { /** * IO线程池最大线程数 */ public static final int IO_MAX Math.max(2, CPU_COUNT * 2); /** * 空闲保活时限单位秒 */ public static final int KEEP_ALIVE_SECONDS 30; /** * 有界队列size */ public static final int QUEUE_SIZE 10000; //线程池 用于IO密集型任务 public static final ThreadPoolExecutor EXECUTOR new ThreadPoolExecutor( IO_MAX, IO_MAX, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, new LinkedBlockingQueue(QUEUE_SIZE), new ThreadUtil.CustomThreadFactory(io)); public static ThreadPoolExecutor getInnerExecutor() { return EXECUTOR; } static { log.info(线程池已经初始化); EXECUTOR.allowCoreThreadTimeOut(true); //JVM关闭时的钩子函数 Runtime.getRuntime().addShutdownHook( new ShutdownHookThread(IO密集型任务线程池, new CallableVoid() { Override public Void call() throws Exception { //优雅关闭线程池 shutdownThreadPoolGracefully(EXECUTOR); return null; } })); }
}有以下几点需要注意 调用allowCoreThreadTimeOut传入了参数true应用于核心线程当池中的线程长时间空闲时可以自行销毁。使用有界队列缓冲任务而不是无界队列如果128太小可以根据具体需要进行增大但是不能使用无界队列。corePoolSize和maximumPoolSize保持一致使得在接收到新任务时如果没有空闲工作线程就优先创建新的线程去执行新任务而不是优先加入阻塞队列等待现有工作线程空闲后再执行。使用JVM关闭时的钩子函数优雅地自动关闭线程池。 2. CPU密集型任务的线程数
CPU密集型任务也叫计算密集型任务其特点是要进行大量计算而需要消耗CPU资源比如计算圆周率、对视频进行高清解码等。 CPU密集型任务虽然也可以并行完成但是并行的任务越多花在任务切换的时间就越多CPU执行任务的效率就越低所以要最高效地利用CPUCPU密集型任务并行执行的数量应当等于CPU的核心数。 /** * CPU核数 **/
public static final int CPU_COUNT Runtime.getRuntime().availableProcessors(); public static final int MAXIMUM_POOL_SIZE CPU_COUNT; //线程池 用于CPU密集型任务
private static final ThreadPoolExecutor EXECUTOR new ThreadPoolExecutor( MAXIMUM_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, new LinkedBlockingQueue(QUEUE_SIZE), new CustomThreadFactory(cpu)); public static ThreadPoolExecutor getInnerExecutor() { return EXECUTOR;
} static { log.info(线程池已经初始化); EXECUTOR.allowCoreThreadTimeOut(true); //JVM关闭时的钩子函数 Runtime.getRuntime().addShutdownHook( new ShutdownHookThread(CPU密集型任务线程池, new CallableVoid() { Override public Void call() throws Exception { //优雅关闭线程池 shutdownThreadPoolGracefully(EXECUTOR); return null; } }));
}3. 混合型任务的线程数
混合型任务既要执行逻辑计算又要进行大量非CPU耗时操作如RPC调用、数据库访问、网络通信等所以混合型任务CPU的利用率不是太高非CPU耗时往往是CPU耗时的数倍。
比如在Web应用中处理HTTP请求时一次请求处理会包括DB操作、RPC操作、缓存操作等多种耗时操作。一般来说一次Web请求的CPU计算耗时往往较少大致在100500毫秒而其他耗时操作会占用5001000毫秒甚至更多的时间。
在为混合型任务创建线程池时如何确定线程数呢业界有一个比较成熟的估算公式具体如下 最佳线程数 (线程等待时间线程CPU时间 / 线程CPU时间) * CPU核数
通过公式可以看出等待时间所占的比例越高需要的线程就越多CPU耗时所占的比例越高需要的线程就越少。 下面举一个例子
比如在Web服务器处理HTTP请求时假设平均线程CPU运行时间为100毫秒而线程等待时间比如包括DB操作、RPC操作、缓存操作等为900毫秒如果CPU核数为8那么根据上面这个公式估算如下
900毫秒 100毫秒 / 100毫秒 * 8 10 * 8 80二. 线程数越多越好吗
很多小伙伴认为线程数越高越好。那么使用很多线程是否就一定比单线程高效呢答案是否定的。
虽然多线程在一些并发场景下能带来性能提升但过多的线程并不意味着性能必定提升。线程数过高可能导致一些问题 上下文切换Context Switching 每个线程的执行都由操作系统调度线程切换会带来额外的开销。当线程数过多时操作系统频繁地在不同线程间切换导致 上下文切换 成本增加这样反而可能降低系统的整体效率。 资源争用 多线程同时访问共享资源时可能会遇到 资源竞争 和 锁竞争特别是在 CPU 绑定的任务中。线程之间的协作和同步会称为性能瓶颈。 内存开销 每个线程需要占用一定的内存维护线程栈、调度信息等过多的线程会消耗大量的内存和系统资源这可能会导致系统性能下降甚至造成内存溢出。 三. Redis 单线程的高效性
Redis 是一个 单线程 的高性能数据库许多人可能会觉得它的设计不合常理为什么不使用多线程来提升性能呢然而Redis 使用单线程反而能够达到极高的吞吐量这是因为
特点核心内容1. 避免多线程上下文切换单线程模型避免了线程切换的开销任务按顺序处理简化了并发控制避免了锁竞争和死锁问题。2. 非阻塞设计采用事件驱动和 I/O 多路复用技术非阻塞处理请求。如果一个请求需要等待外部资源如网络 I/ORedis 会把控制权交给其他请求而不是阻塞线程。这种方式避免了多线程中因为等待 I/O 资源导致的线程空闲充分利用了 CPU 的时间片。3. CPU vs I/O 密集型Redis 的大多数操作如 GET/SET是 I/O 密集型 的单线程在 I/O 密集型应用中有优势。4. 数据访问模式Redis 操作主要是内存访问内存操作速度快单线程执行时没有同步问题数据结构如哈希表、跳表等高效。