首页
技术小册
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并发编程实战
### 34 | Worker Thread模式:如何避免重复创建线程? 在Java并发编程中,线程是执行并发任务的基本单位。然而,频繁地创建和销毁线程不仅效率低下,还会因为线程创建的开销(包括内存分配、上下文切换等)而严重影响程序的性能。为了解决这一问题,Worker Thread模式应运而生。该模式旨在通过重用一组固定数量的线程来执行多个并发任务,从而避免重复创建线程,提高资源利用率和程序性能。 #### 一、Worker Thread模式概述 Worker Thread模式,也称为线程池模式,是一种常用的并发设计模式。它通过一个共享的线程池来管理一组工作线程(Worker Threads),这些线程可以循环地执行提交给线程池的任务。当有新任务到来时,线程池会尝试利用现有的空闲线程来执行该任务,如果所有线程都在忙碌,则根据线程池的配置策略(如阻塞等待、拒绝新任务等)来处理新任务。 Worker Thread模式的核心优势在于: 1. **减少线程创建和销毁的开销**:通过重用线程,避免了频繁创建和销毁线程所带来的性能损耗。 2. **提高响应速度**:由于线程已经预先创建并等待任务,因此可以更快地响应新任务。 3. **提高资源利用率**:通过合理管理线程数量,可以避免因线程过多而导致的系统资源耗尽问题。 4. **提供灵活的并发控制**:线程池允许开发者根据实际需求调整线程数量,从而控制并发级别。 #### 二、Worker Thread模式的实现 在Java中,`java.util.concurrent`包提供了强大的线程池支持,其中`ExecutorService`接口是线程池的核心接口。通过实现或扩展该接口,可以方便地创建和管理线程池。 ##### 1. 线程池的创建 Java提供了几种预定义的线程池实现,如`Executors`工厂类中的方法,可以快速创建不同类型的线程池: - **FixedThreadPool**:创建一个固定大小的线程池,可控制并发执行的线程数,超出的线程会在队列中等待。 - **CachedThreadPool**:创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需的线程,那么它就会回收空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能地添加新线程来处理任务。 - **SingleThreadExecutor**:创建一个单线程的线程池,它用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 - **ScheduledThreadPool**:创建一个支持定时及周期性任务执行的线程池。 示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 提交任务到线程池执行 for (int i = 0; i < 10; i++) { int taskId = i; executor.submit(() -> { System.out.println(Thread.currentThread().getName() + " is processing task " + taskId); // 模拟任务执行时间 try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } // 关闭线程池(不再接受新任务,但已提交的任务会继续执行) executor.shutdown(); // 等待所有任务完成(可选) // executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } } ``` ##### 2. 线程池的配置 线程池的配置主要包括线程数量、任务队列类型及容量、拒绝策略等。合理配置这些参数对于优化线程池性能至关重要。 - **线程数量**:应根据CPU核心数、任务性质(CPU密集型或IO密集型)及系统资源情况来确定。 - **任务队列**:Java提供了几种不同类型的阻塞队列作为任务队列,如`ArrayBlockingQueue`、`LinkedBlockingQueue`、`SynchronousQueue`等。选择合适的队列类型可以影响线程池的调度策略和性能。 - **拒绝策略**:当线程池和队列都满了时,需要定义一种策略来处理新提交的任务。Java提供了几种预定义的拒绝策略,如`AbortPolicy`(直接抛出异常)、`CallerRunsPolicy`(由调用者线程执行该任务)、`DiscardOldestPolicy`(丢弃队列中最老的任务,然后尝试提交新任务)、`DiscardPolicy`(直接丢弃新任务)。 ##### 3. 线程池的使用注意事项 - **合理关闭线程池**:使用`shutdown()`或`shutdownNow()`方法来关闭线程池。前者会等待所有已提交的任务执行完毕,后者会尝试停止所有正在执行的任务并返回等待执行的任务列表。 - **避免创建大量线程**:过多的线程会消耗大量系统资源,导致性能下降。应根据实际情况合理设置线程池大小。 - **注意任务间的依赖关系**:如果任务之间存在依赖关系,需要谨慎使用线程池,因为线程池中的线程是并行执行的,可能会破坏任务间的依赖顺序。 - **监控和调优**:对于生产环境中的线程池,应定期进行监控和调优,以确保其性能最优。 #### 三、Worker Thread模式的应用场景 Worker Thread模式广泛应用于需要并发处理大量任务的场景,如Web服务器、数据库连接池、文件处理、网络通信等。以下是一些具体的应用示例: - **Web服务器**:Web服务器需要处理大量的HTTP请求,每个请求都可以视为一个任务。通过Worker Thread模式,可以高效地处理这些请求,提高服务器的并发处理能力。 - **数据库连接池**:数据库连接是昂贵的资源,频繁地创建和销毁连接会严重影响性能。通过Worker Thread模式,可以重用连接池中的连接,减少连接创建和销毁的开销。 - **文件处理**:在处理大量文件时,可以将每个文件的处理任务提交给线程池执行,以提高文件处理的效率。 - **网络通信**:在网络编程中,经常需要处理大量的网络请求和响应。通过Worker Thread模式,可以并行处理这些请求和响应,提高网络通信的吞吐量和响应速度。 #### 四、总结 Worker Thread模式是一种有效的并发设计模式,它通过重用一组固定数量的线程来执行多个并发任务,从而避免了重复创建线程所带来的性能损耗。在Java中,`java.util.concurrent`包提供了强大的线程池支持,使得实现Worker Thread模式变得简单而高效。通过合理配置线程池的参数和注意使用过程中的一些细节问题,可以充分发挥Worker Thread模式的优势,提高程序的并发处理能力和性能。
上一篇:
33 | Thread-Per-Message模式:最简单实用的分工方法
下一篇:
35 | 两阶段终止模式:如何优雅地终止线程?
该分类下的相关小册推荐:
Java语言基础6-面向对象高级
Java语言基础8-Java多线程
经典设计模式Java版
Java语言基础15-单元测试和日志技术
java源码学习笔记
Java语言基础12-网络编程
Java语言基础2-运算符
Java语言基础13-类的加载和反射
Java语言基础1-基础知识
Java语言基础3-流程控制
SpringBoot零基础到实战
Mybatis合辑1-Mybatis基础入门