跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

在Java里如何实现一个任务管理系统_Java多线程与数据持久化应用

必须用ExecutorService管理任务生命周期,禁用new Thread();生产环境显式构造ThreadPoolExecutor并配置有界队列与CallerRunsPolicy;状态需持久化至数据库并事务更新;任务中定期检查中断并双重校验取消;CompletableFuture编排依赖时避免join()阻塞主线程;MyBatis插入用useGeneratedKeys安全获取主键。 用
ExecutorService
管理任务生命周期,别直接 new Thread() 手动创建
Thread
对象启动任务会导致资源失控、无法统一回收、异常丢失。实际项目中必须用线程池封装执行逻辑。 推荐使用
ThreadPoolExecutor
或其工厂类
Executors
(注意:仅限开发/测试快速验证,生产环境应显式构造以避免
LinkedBlockingQueue
无界队列引发 OOM):
ExecutorService executor = new ThreadPoolExecutor( 2, 4, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadFactoryBuilder().setNameFormat("task-pool-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy() );
CallerRunsPolicy
是关键兜底策略:当队列满且线程数已达上限时,由提交任务的线程自己执行该任务,防止任务无限积压 务必调用
executor.shutdown()
+
awaitTermination()
做优雅停机,否则 JVM 可能不退出 每个任务应捕获并处理
Throwable
,否则未检查异常会静默吞掉,导致任务“消失” 任务状态用枚举 + 数据库字段联合建模,别只靠内存标记 仅用
volatile boolean isRunning
这类内存变量无法支撑重启恢复、多实例协同或审计需求。必须将状态持久化到数据库,并与内存状态做同步校验。 典型表结构建议包含:
id
、
status
(
TASK_STATUS
枚举:PENDING / RUNNING / SUCCESS / FAILED / CANCELLED)、
created_at
、
started_at
、
finished_at
、
error_message
(TEXT 类型,存异常堆栈摘要): 立即学习 “ Java免费学习笔记(深入) ”; Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 状态变更必须走事务:先更新 DB 的
status
,再触发业务逻辑;失败则回滚,避免状态漂移 轮询检查任务进度时,不要依赖
SELECT * FROM task WHERE status = 'RUNNING'
全表扫描,加
WHERE updated_at > ?
时间范围过滤 若支持取消,需在任务执行体中定期检查
Thread.currentThread().isInterrupted()
,并配合 DB 中
status = 'CANCELLED'
双重判断 用
CompletableFuture
编排依赖任务,但慎用
join()
当任务存在先后依赖(如“导出报表”需等“生成数据”完成),
CompletableFuture
比手动维护回调链更清晰。但它的阻塞方法容易埋雷。 避免在 Web 请求线程中调用
cf.join()
或
cf.get()
:会阻塞 Tomcat/NIO 线程,迅速拖垮吞吐量 用
thenApplyAsync(..., executor)
显式指定异步执行器,确保后续步骤不挤占主线程资源 组合多个任务时,优先用
allOf()
+
thenAccept()
而非循环
join()
,前者是真正并行等待,后者是串行阻塞 异常传播要主动处理:
exceptionally()
或
handle()
必须覆盖,否则上游无法感知下游失败 MyBatis 插入任务记录时,用
useGeneratedKeys
获取主键,别查两次 插入新任务后通常要立即拿到 ID 去更新状态或关联子任务。如果先
INSERT
再
SELECT LAST_INSERT_ID()
,在高并发下可能拿错 ID(尤其 MySQL 使用
REPLACE INTO
或批量插入场景)。 MyBatis 正确做法是在
标签中声明:
INSERT INTO task (status, created_at) VALUES (#{status}, #{createdAt})
keyProperty="id"
必须与实体类字段名完全一致(区分大小写),否则赋值失败且无报错 PostgreSQL 需额外指定
keyColumn="id"
,因为其序列名和字段名常不一致 批量插入时
useGeneratedKeys
仍有效,但要求 JDBC 驱动版本 ≥ 4.2 且数据库配置允许返回多行生成键 跨服务或长时间运行任务的状态同步,不能只靠单库事务。最终一致性才是现实选择——比如用本地消息表 + 定时扫描,而不是强求所有操作包在一个 @Transactional 里。

相关文章