首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
当前位置:
首页>>
技术小册>>
Java并发编程实战
小册名称:Java并发编程实战
### 22 | Executor与线程池:如何创建正确的线程池? 在Java并发编程中,线程池(ThreadPool)是一种基于池化技术来管理线程的生命周期、复用线程资源并减少线程创建和销毁开销的重要机制。`java.util.concurrent`包中的`Executor`框架为我们提供了强大的线程池支持,使得并发编程更加高效和易于管理。本章将深入探讨如何根据实际需求创建合适的线程池,包括理解线程池的基本概念、选择正确的线程池类型、配置线程池参数以及避免常见陷阱。 #### 22.1 理解线程池基础 线程池是一种能够管理一组同构工作线程的资源池,这些线程可以执行提交给它的任务。线程池的主要优势包括: - **资源复用**:通过复用现有线程而不是每次执行新任务时都创建新线程,减少了线程的创建和销毁开销。 - **提高响应速度**:当任务到达时,线程池中的线程可以立即响应,无需等待新线程的创建。 - **线程管理**:通过线程池,可以方便地管理线程的生命周期、数量以及执行策略,如设置线程优先级、线程守护状态等。 - **并发控制**:可以控制同时执行的线程数量,有助于避免过多的线程竞争资源导致的系统资源耗尽。 #### 22.2 Executor框架简介 `Executor`框架是Java并发包中的核心之一,它提供了一系列用于任务执行、任务调度、线程池管理等功能的类和接口。`Executor`是一个接口,它定义了执行已提交任务的方法。而`ExecutorService`接口则扩展了`Executor`接口,增加了任务管理的能力,如关闭线程池、提交Callable任务并获取结果等。 #### 22.3 线程池的类型与选择 Java并发包提供了几种不同的线程池实现,每种实现都适用于不同的场景。了解这些类型及其特点,是创建正确线程池的前提。 1. **FixedThreadPool**:固定大小的线程池。如果所有线程都忙于执行任务,新任务将在队列中等待,直到有线程空闲。适合执行长期运行的任务,且任务数量相对稳定的场景。 2. **CachedThreadPool**:可缓存的线程池。当线程池中的线程数量超过处理任务所需的线程数时,多余空闲线程将在一段时间后自动终止。如果后续需要,则重新创建新线程。适合执行大量短期异步任务,可以提高程序响应速度。 3. **ScheduledThreadPool**:支持定时及周期性任务执行的线程池。它允许你调度命令在给定的延迟后运行,或者定期地执行。 4. **SingleThreadExecutor**:单线程化的Executor。它使用单个工作线程来执行任务,保证所有任务按照指定顺序(FIFO、LIFO、优先级)执行。适用于需要按顺序执行任务的场景。 5. **ForkJoinPool**:一种特殊的线程池,用于执行可以分解为更小任务的任务。它使用分而治之的策略来并行处理大规模计算任务。 #### 22.4 创建线程池的步骤 1. **分析需求**:首先,明确你的应用需求,包括任务类型(CPU密集型、IO密集型、定时任务等)、任务执行时间、任务数量等。 2. **选择线程池类型**:根据需求分析结果,选择合适的线程池类型。 3. **配置线程池参数**: - **核心线程数(corePoolSize)**:线程池中保持存活的最少线程数。 - **最大线程数(maximumPoolSize)**:线程池中允许的最大线程数。 - **存活时间(keepAliveTime)**:当线程数大于核心线程数时,这是多余空闲线程在终止前等待新任务的最长时间。 - **工作队列(workQueue)**:用于存放待执行的任务的阻塞队列。常见的队列类型有`ArrayBlockingQueue`、`LinkedBlockingQueue`、`SynchronousQueue`等。 - **线程工厂(ThreadFactory)**:用于创建新线程的工厂,可以通过它设置线程名称、优先级等属性。 - **拒绝策略(RejectedExecutionHandler)**:当线程池和队列都满了时,对新任务的处理策略,如抛出异常、直接丢弃、丢弃最旧任务等。 4. **创建并启动线程池**:使用`Executors`工厂类提供的静态方法或直接实例化`ThreadPoolExecutor`来创建线程池,并启动执行任务。 5. **监控与调优**:通过监控线程池的运行状态(如活跃线程数、任务队列长度等),调整线程池参数以达到最优性能。 #### 22.5 避免常见陷阱 1. **资源耗尽**:不当的线程池配置(如过大的最大线程数)可能导致系统资源耗尽,影响应用性能甚至导致系统崩溃。 2. **死锁**:在提交给线程池的任务中,应避免使用同步原语(如synchronized、ReentrantLock等)不当导致的死锁。 3. **任务拒绝**:当线程池和队列都满时,新任务将被拒绝执行。应合理设置拒绝策略,或监控任务提交情况,及时调整线程池配置。 4. **线程泄露**:如果线程池中的线程在执行任务时,由于某些原因(如等待外部资源、持有锁等)而无法结束,将导致线程泄露。应确保任务能够正常结束,或设置合理的存活时间让空闲线程自动终止。 5. **上下文切换开销**:过多的线程会导致频繁的上下文切换,降低系统性能。应根据任务类型和硬件条件合理设置线程数量。 #### 22.6 总结 创建正确的线程池是Java并发编程中的一项重要技能。通过深入理解线程池的基本概念、选择合适的线程池类型、合理配置线程池参数以及避免常见陷阱,我们可以有效地利用线程池来提高程序的并发性能和响应速度。在实际应用中,还需要根据应用的具体需求和运行环境进行灵活的调整和优化。
上一篇:
21 | 原子类:无锁工具类的典范
下一篇:
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
该分类下的相关小册推荐:
Mybatis合辑1-Mybatis基础入门
Java语言基础15-单元测试和日志技术
Java并发编程
Java必知必会-Maven初级
Mybatis合辑4-Mybatis缓存机制
JAVA 函数式编程入门与实践
Java语言基础13-类的加载和反射
经典设计模式Java版
Java必知必会-Maven高级
Java语言基础3-流程控制
Java性能调优实战
Java语言基础2-运算符