Java线程池的几种实现方法和区别介绍
什么是线程池
在线程池中,线程的创建和销毁都由线程池管理器来处理,线程池中包括一组线程,线程池会根据配置的参数来动态调整线程池中线程的数量。线程池中的线程可以被多个任务共享,使线程的创建和销毁开销及竞争锁等问题得以优化。
为什么要使用线程池
线程池的主要目的是为了控制并发执行的线程数,有以下几个优点:
- 降低线程的创建和销毁的开销。
- 重复利用已经创建的线程,避免创建新的线程所带来的开销。
- 避免大量线程抢占系统资源导致系统负载过大而崩溃的风险。
- 更好地管理线程,提供更加灵活,强大的线程管理能力。
线程池的实现方法
在Java中,线程池是由java.util.concurrent包提供的,其中提供了多种类型的线程池,常用的有以下几种:
- newFixedThreadPool
- newCachedThreadPool
- newScheduledThreadPool
- newSingleThreadExecutor
- ThreadPoolExecutor
newFixedThreadPool
newFixedThreadPool(int nThreads)
方法创建一个有固定大小的线程池,每次提交一个任务就创建一个线程,直到线程数量达到线程池的最大容量。线程池的大小一旦达到最大值,就会保持不变,如果有多余的任务需要执行,就必须等待线程池有空闲的线程才能执行。
示例代码:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
fixedThreadPool.execute(new Task(i));
}
该示例代码会创建一个大小为5的线程池,接着往线程池中提交了10个任务。由于线程池的大小为5,因此最多同时执行5个任务,剩下的任务会等待有空闲的线程再去执行。
newCachedThreadPool
newCachedThreadPool()
方法创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,会自动添加新的线程来处理任务。
示例代码:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
cachedThreadPool.execute(new Task(i));
}
该示例代码会创建一个可缓存的线程池,接着往线程池中提交了10个任务。由于线程池的大小没有限制,因此可以同时执行10个任务。
newScheduledThreadPool
newScheduledThreadPool(int corePoolSize)
方法创建一个固定大小的线程池,可以定时或延时执行任务。该方法返回一个ScheduledExecutorService对象,该对象可以在指定延时后执行任务或按固定延时周期执行任务。
示例代码:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Task(1), 2, TimeUnit.SECONDS);
scheduledThreadPool.scheduleAtFixedRate(new Task(2), 2, 1, TimeUnit.SECONDS);
该示例代码会创建一个大小为5的定时线程池,接着往线程池中提交了两个任务,其中第一个任务会在2秒后执行一次,第二个任务则会在2秒后开始执行,每隔1秒执行一次。
newSingleThreadExecutor
newSingleThreadExecutor()
方法创建一个单线程的线程池,所有任务都在同一个线程中执行,保证了所有任务的顺序执行,也避免了线程间的竞争问题。
示例代码:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
singleThreadExecutor.execute(new Task(i));
}
该示例代码会创建一个单线程的线程池,接着往线程池中提交了10个任务。由于是单线程的线程池,因此所有任务都会按照提交的顺序依次执行。
ThreadPoolExecutor
ThreadPoolExecutor是通过Executors工厂类提供的方法之外,直接创建线程池的实现类,这个类的构造方法比较复杂,需要传入7个参数,分别是:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 线程最大空闲时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 等待队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
示例代码:
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
1, // 线程最大空闲时间
TimeUnit.MINUTES, // 时间单位
queue, // 等待队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(new Task(i));
}
该示例代码会手动创建一个线程池,该线程池的核心线程数为2,最大线程数为4,空闲线程最长等待时间为1分钟,等待队列容量为10,拒绝策略为丢弃任务并抛出异常。接着往线程池中提交了10个任务,在这个示例中,因为线程池容量不够,多余的任务会被等待队列中的消息存储,直到有空闲线程时,线程池才会将消息从等待队列中取走,进行任务处理。
总结
以上就是Java线程池的几种实现方法及其区别介绍。根据不同需求,选择合适的线程池会更好地提升系统性能、压力测试和控制流量。
参考文献
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java线程池的几种实现方法和区别介绍实例详解 - Python技术站