java线程池学习(三) —— ThreadPoolExecutor

本文深入探讨了Java提供的线程池实现——ExecutorService接口及其内部类ThreadPoolExecutor的使用方法,包括如何初始化线程池、核心线程数、最大线程数、空闲线程最大存活时间和任务队列等关键属性,并详细介绍了execute()、submit()和submit(Callable)方法的使用。同时,通过实例展示了如何利用ThreadPoolExecutor创建线程池并处理任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章中我们自己写了一个简单的线程池。

这一篇文章我们来了解一下java为我们提供的线程池实现—— ExecutorService接口

它位于jdk的java.util.concurrent包下。

JDK提供了这么两个类来实现这个接口:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

我们这篇文章只介绍一下ThreadPoolExecutor类(ScheduledThreadPoolExecutor类类似,多加入了计划任务功能。)

我们首先看看怎么用ThreadPoolExecutor类初始化一个线程池:

//初始化一个线程池
//核心线程数
int  corePoolSize  = 5;
//最大线程数
int  maxPoolSize   = 10;
//空闲线程最大存活时间
long keepAliveTime = 5000;
//任务队列
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(5);
        
ExecutorService threadPoolExecutor =
        new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                TimeUnit.MILLISECONDS,
                queue
        );

这个类完全实现了一个类似于我们上一篇文章中实现的线程池,它包含以下几个属性:

1. corePoolSize 

 核心线程数:即使没有任何任务过来,线程池里面也会有保持的最基本线程数。

 2. maximumPoolSize

 最大线程数(即使任务特别多,线程池里的线程数也不会超过它)

 3. keepAliveTime

 空闲线程最大存活时间

 4. blockingQueue

 任务队列,用来存放待处理的任务。我们在这个系列的第一篇文章中就介绍过了它。

 可以选择以下几个阻塞队列。

  1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。
  3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。
  4. PriorityBlockingQueue:一个具有优先级得无限阻塞队列。  

那么整个类的结构就如下图所示:

要使用这个线程池的话,可以使用它提供给我们的如下方法:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(...)
  • invokeAll(...)

execute()和submit()可以向这个线程池提交单个任务。他们的区别是:

使用execute提交任务,但是execute方法没有返回值,所以无法判断任务知否被线程池执行成功。

使用submit 方法来提交任务,它会返回一个future对象,那么我们可以通过这个future对象来判断任务是否执行成功

invokeAll可以直接把一个List类型的任务列表一次性的提交给线程池执行。

==================================================================================

那么接下来我们就用 ThreadPoolExecutor 来创建一个线程池,改写一下我们上一篇文章的例子:

public class ProblemCreater {

	public static void main(String[] args) throws Exception {
		//初始化线程池
		//核心线程数
		int  corePoolSize  = 5;
		//最大线程数
		int  maxPoolSize   = 10;
		long keepAliveTime = 5000;
                
		ExecutorService threadPoolExecutor =
		        new ThreadPoolExecutor(
		                corePoolSize,
		                maxPoolSize,
		                keepAliveTime,
		                TimeUnit.MILLISECONDS,
		                new ArrayBlockingQueue<Runnable>(5)
		        );
		
		//生成者不断产生任务
		for(int i=1;i<10;i++){
			//定义一个新的任务
			Runnable task = new Runnable(){
				public void run(){
					Random random = new Random();
					//随机一个数字模拟需要解决的时间
					int randomTime = Math.abs(random.nextInt())%20;
					try {
						Thread.sleep(randomTime*1000);
						System.out.println("任务完成,花费时间为:"+randomTime);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			//将问题插入到线程池任务队列中
			threadPoolExecutor.execute(task);
			System.out.println("插入新的任务"+i);
		}
	}
}

我们再回头看看这个 ThreadPoolExecutor 的初始化,我们需要给它传递5个参数。核心线程数,最大线程数,线程存活时间,时间单位,还有阻塞队列的类型。

这个过程还是比较繁琐的。其实Java帮我们简化了这个过程,我们可以根据不同的情景,直接用一行代码创建一个合适的线程池。

实现这个功能的就是 java.util.concurrent.Executors类。我们在下一篇文章中再详细介绍这个类的用法。


### Java 中 `ThreadPoolExecutor` 类的使用方法 #### 创建自定义线程池并执行任务 通过继承 `CustomThreadPool` 并实现其功能来展示如何创建和配置一个简单的线程池。下面的例子展示了如何初始化具有两个核心线程数目的线程池,并向其中提交五个任务。 ```java package com.howtodoinjava.threads; public class CustomThreadPoolExample { public static void main(String[] args) { // 初始化带有2个工作线程的核心线程池 CustomThreadPool customThreadPool = new CustomThreadPool(2); for (int i = 1; i <= 5; i++) { Task task = new Task("Task " + i); System.out.println("Created : " + task.getName()); // 提交任务给线程池去执行 customThreadPool.execute(task); } } } ``` 此代码片段说明了如何构建基本的任务调度框架[^1]。 #### 利用标准库中的工具简化线程池管理 为了更方便地管理和操作线程池,可以借助于 `Executors` 工具类提供的静态工厂方法。这些方法允许快速设置不同类型的预设线程池实例,而无需手动指定所有的参数细节。 ```java import java.util.concurrent.*; // 使用 Executors 来获取固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(4); for(int i=0;i<10;i++){ Runnable worker = new WorkerThread("" + i); // 将任务交给线程池处理 executor.execute(worker); } // 关闭线程池以防止新任务被接受 executor.shutdown(); while (!executor.isTerminated()) {} System.out.println("Finished all threads"); ``` 这段程序利用了 `newFixedThreadPool()` 方法来获得拥有四个工作线程的标准线程池对象[^2]。 #### 定义具体的工作单元——事件处理器 当涉及到具体的业务逻辑时,则可以通过像 `EventHandler.handle()` 这样的函数来进行封装。每当有新的工作任务到来时,就可以调用该方法完成相应的数据处理流程。 ```java public class WorkEvent { private String workNo; // 构造器和其他成员省略... public String getWorkNo(){ return this.workNo; } } public class EventHandler { /** * 处理业务 */ public static void handle(WorkEvent workEvent){ System.out.println("正在处理,workNo=[" + workEvent.getWorkNo() + "]"); } } ``` 上述代码段描述了一个典型的业务场景下的任务接收与响应机制[^3]。 #### 结合实际案例理解概念 对于初学者来说,观看一些关于 `ThreadPoolExecutor` 应用的教学视频可能会更加直观易懂。例如,在 Bilibili 上有一个专门介绍这个主题的内容可以帮助加深印象[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值