介绍
当你需要在程序中处理大量同类型的耗时任务,并且需要监听每个任务的工作状态和任务的编号,能及时响应并处理错误,而且需要所有任务都执行完毕自动通知时,那么这个任务调度器就非常适合你!
它封装了 Java
的 ThreadPoolExecutor
和 ScheduledExecutorService
,提供了一种简便的方式来管理并行任务的执行,并在所有任务完成后自动关闭执行器。
之前写了一个串行任务调度器,这篇也是根据这个改编而来:一个用Kotlin编写简易的串行任务调度器
使用方法
1.初始化:
val taskListener = object : ParallelTaskExecutor.TaskListener {
override fun beforeExecute(task: ParallelTaskExecutor.NamedRunnable) {
println("开始任务:${
task.name}")
}
override fun afterExecute(task: ParallelTaskExecutor.NamedRunnable, exception: Exception?) {
println("完成任务:${
task.name},异常:$exception")
}
}
val taskExecutor = ParallelTaskExecutor(taskListener, 1)
2.提交任务:
taskExecutor.submit("加载数据") {
// 加载数据的代码
}
taskExecutor.submit("处理数据") {
// 处理数据的代码
}
//或者
for (i in 0..15) {
taskExecutor.submit("task:$i") {
Thread.sleep(3000)
}
}
...
3.优雅关闭:
当所有任务完成后,调度器将在指定的超时后自动关闭,确保不浪费资源。
完整代码
/**
* ParallelTaskExecutor 是一个用于管理和并行执行任务的类。
* 它确保所有任务都被执行,并提供一个机制在所有任务完成时通知。
*/
class ParallelTaskExecutor(private val listener: TaskListener? = null, private val timeout: Long = 5) {
// 用于存储任务的队列
private val taskQueue = ConcurrentLinkedQueue<NamedRunnable>()
// 标记当前是否有任务正在处理
private val isTaskRunning = AtomicBoolean(false)
// 用于并行运行任务的执行器服务
private var executorService: ExecutorService? = null
// 用于安排执行器服务关闭的调度执行器服务
private val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
// 用于安排执行器服务关闭的 Future
private var scheduledShutdownFuture: ScheduledFuture<*>? = null
// 计数器,用于跟踪运行中的任务数量
private val runningTasksCounter = AtomicInteger(0)
/**
* 提交一个任务到执行器。
* @param name 任务的名称。
* @param task 要执行的任务。
*/
@Synchronized
fun submit(name: String, task: Runnable) {
ensureExecutorService() // 确保执行器服务已启动
taskQueue.offer(NamedRunnable(name, task)) // 将任务添加到队列中
// 取消之前安排的任何关闭操作
scheduledShutdownFuture?.cancel(false)
// 如果当前没有运行的任务,开始处理任务
if (isTaskRunning.compareAndSet(false, true)) {
processTasks()
}
}
/**
* 处理队列中的任务。
*/
private fun processTasks() {
try {
while (taskQueue.isNotEmpty()) {
// 从队列中获取下一个任务
val nextTask = taskQueue.poll()
// 递增运行任务计数器
runningTasksCounter.incrementAndGet()
// 将任务提交到执行器服务
executorService?.execute {
listener?.beforeExecute(nextTask)
var exception: Exception? = null
try {
nextTask.run()
} catch (e: Exception) {
e.printStackTrace()
exception = e
}
listener?.afterExecute(nextTask, exception)
// 任务执行完毕后递减计数器,并检查是否所有任务都已完成
if (runningTasksCounter.decrementAndGet() == 0) {
scheduleShutdown()
}
}
}
} finally {
// 重置 isTaskRunning 标志
isTaskRunning.set(false)
}
}
/**
* 确保执行器服务已初始化并在运行中。
*/
private fun ensureExecutorService() {
if (executorService == null || executorService!!.isShutdown) {
executorService = Executors.newFixedThreadPool(20)
}
}
/**
* 如果所有任务都已完成,安排关闭执行器服务。
*/
private fun scheduleShutdown() {
// 如果任务队列为空且没有运行中的任务,则安排关闭
if (taskQueue.isEmpty() && runningTasksCounter.get() == 0) {
scheduledShutdownFuture = scheduler.schedule({
executorService?.shutdown()
executorService = null
listener?.onShutdown()
}, timeout, TimeUnit.SECONDS)
}
}
/**
* 任务监听器接口,提供任务执行事件的回调。
*/
interface TaskListener {
fun beforeExecute(task: NamedRunnable)
fun afterExecute(task: NamedRunnable, exception: Exception?)
fun onShutdown()
}
/**
* NamedRunnable 是一个对 Runnable 任务的包装,包含一个用于标识的名称。
*/
class NamedRunnable(val name: String, private val task: Runnable) : Runnable {
override fun run() {
task.run()
}
}
}
最后
- 可以根据你的需求自由定义线程池最大线程数
- 资源自动管理,超时自动释放资源,新加任务自动创建线程池。
- 自定义超时时间,可以延迟线程池资源释放,等待新任务到来,合理利用。