02.activiti第一天: 09-10.流程实例, 任务, 执行对象控制流程的执行

本文深入探讨了工作流引擎的核心概念,包括流程引擎的初始化、流程定义的部署、流程实例的启动与查询,以及任务的管理和完成。通过示例代码展示了如何使用流程引擎API进行流程操作,涉及流程定义、流程实例、任务和个人任务的查询与管理。

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

项目: cn.itcast.c_processInstance

1. 流程核心对象-流程引擎: ProcessEngine

2. 部署流程的定义(通过zip)

     @Test
     public void deploymentProcessDefinition_zip(){
     	InputStream in = this.getClass().getClassLoader().getResourceAsStream("diagrams/helloworld.zip");
     	ZipInputStream zipInputStream = new ZipInputStream(in);
     	Deployment deployment = processEngine.getRepositoryService()// 与流程定义和部署对象相关的Service
     			.createDeployment()// 创建一个部署对象
     			.name("流程定义")// 添加部署名称
     			.addZipInputStream(zipInputStream)// 指定zip格式的文件完成部署
     			.deploy();// 完成部署
     	System.out.println("部署ID: " + deployment.getId());
     	System.out.println("部署名称: " + deployment.getName());

     } 

部署对象和流程定义相关的表
act_re_deployment     部署对象表
act_re_procdef      流程定义表
act_ge_bytearray     资源文件表
act_ge_property       主键生成策略表

3. 启动流程实例

  @Test
  public void startProcessInstance(){
  	// 流程定义的key(可查看流程定义表)
  	String processDefinitionKey = "helloworld";
  	ProcessInstance pi = processEngine.getRuntimeService()// 与正在执行的流程实例和执行对象相关的Service
  	    // 使用流程定义的key启动流程实例, key对应helloworld.bpmn文件中id的属性值
  			.startProcessInstanceBykey(processDefinitionKey);
  	System.out.println("流程实例ID: " + pi.getId());// 流程实例ID 1001(?到底是执行对象ID还是流程实例ID(PROC_INST_ID))
  	System.out.println("流程定义ID: " + pi.getProcessDefinitionId());// 流程定义ID helloworld:1:4

  }
  • 注1: 不采用流程实例ID启动, 是因为key是个定值, ID是个生成值, 且使用key启动会默认启动最新版本.

流程实例, 执行对象, 任务相关表
act_ru_execution  正在执行的执行对象表
act_hi_procinst     流程实例的历史表
act_ru_task    正在执行的任务表(只有节点是UserTask的时候, 该表存在数据)
act_hi_taskinst   历史任务表(只有节点是UserTask的时候, 该表存在数据, 包含完成和未完成的)
act_hi_actinst     所有活动节点的历史表(相对历史任务表, 包括非任务节点)

  • 注2: 查询act_ru_execution发现ID(执行对象ID)PROC_INST_ID(流程实例ID) 两字段相同, 但其实实际情况 并不一定相同 1.
  • 流程实例 processInstance执行对象 execution:
    在这里插入图片描述

  • 流程实例的历史表 act_hi_procinst 结构:
    在这里插入图片描述

  • 正在执行的任务表 act_ru_task的表结构:
    在这里插入图片描述

  • 历史任务表 act_hi_taskinst的表结构:

在这里插入图片描述
在这里插入图片描述

  • 所有活动节点的历史表 act_hi_actinst的表结构:
    在这里插入图片描述在这里插入图片描述

4. 查询当前人的个人任务

  public void findMyPersonalTask(){
  	String assignee = "王五";
  	List<Task> list = processEngine.getTaskService()// 与正在执行的任务管理相关的Service
  			.createTaskQuery()// 创建任务查询对象
  			/** 查询条件(where部分) */
  			.taskAssignee(assignee)// 指定个人任务查询, 指定办理人
  			//.taskCandidateUser(assignee)// 组任务办理人查询 
  			//.processDefinitionId(processDefinitionId)// 使用流程定义ID查询
  			//.processInstanceId(processInstanceId)// 使用流程实例ID查询
  			//executionId(executionId)// 使用执行对象ID查询
  			/** 排序 */
  			//.orderBy*().asc()// 按照*升序
  			/** 返回结果集 */
  			//.singleResult()// 返回唯一结果集
  			//.count()// 返回结果集的数量
  			//.listPage(firstResult, maxResults)// 分页查询
  			.list();// 返回列表

    if (list != null && list.size() > 0) {
		System.out.println("任务ID: " + task.getId());
		System.out.println("任务名称: " + task.getName());
		System.out.println("任务创建时间: " + task.getCreateTime());
		System.out.println("任务办理人: " + task.getAssignee());
		System.out.println("流程实例ID: " + task.getProcessInstanceId());
		System.out.println("执行对象ID: " + task.getExecutionId());
		System.out.println("流程定义ID: " + task.getProcessDefinitionId());         
	}
  }

5. 完成我的任务

  	  public void completeMyPersonalTask() {
  	  	// 任务ID
  	  	String taskId = "1004";
  	  	processEngine.getTaskService()// 与正在执行的任务管理相关的Service
  	  		.complete(taskId);
  	  	System.out.println("完成任务! 任务ID: " + taskId);
  	  }

6. 查询流程状态(判断流程正在执行, 还是结束了)

  public void getProcessStatus() {
    String processInstanceId = "1001";
    ProcessInstance pi = processEngine.getRuntimeService()// 与正在执行的流程实例和执行对象有关的service
    	.createProcessInstanceQuery()// 创建流程实例查询
    	.processInstanceId(processInstanceId)// 使用流程实例ID查询
    	.singleResult();

    if (pi == null) {
    	System.out.println("流程已经结束.");
    } else {
     	System.out.println("流程正在执行.");
    }
  }

7. 查询历史任务

  public void getHistoryTask() {
    String taskAssignee = "张三";
    List<HistoricTaskInstance> list = processEngine.getHistoryService()// 与历史数据(历史表)相关的service
      .createHistoricTaskInstanceQuery()// 创建历史任务实例查询
      .taskAssignee(taskAssignee)// 指定历史任务的办理人
      .list();
    if (list != null) {
      for (HistoricTaskInstance hti : list) {
      	System.out.println(hti.getId());
      	System.out.println(hti.getName());
      	System.out.println(hti.getProcessInstanceId());
      	System.out.println(hti.getStartTime());
      	System.out.println(hti.getEndTime());
      	System.out.println(hti.getDurationInMillis());
      }
    }

  }  

8. 查询历史流程实例

  public void getHistoryProcessInstance() {
    String processInstanceId = "1001";
    HistoricProcessInstance hpi = processEngine.getHistoryService()// 与历史数据(历史表)相关的Service
      .createHistoricProcessInstanceQuery()//创建历史流程实例查询
      .processInstanceId(processInstanceId)// 使用流程实例ID查询
      .singleResult();
    System.out.println(hpi.getId());// 流程实例ID
    System.out.println(hpi.getProcessDefinitionId());// 流程定义ID
    System.out.println(hpi.getStartTime());
    System.out.println(hpi.getEndTime());
    System.out.println(hpi.getDurationInMillis());// 持续毫秒数
  }

  1. 如果是单例流程(没有分支和聚合), 那么 流程实例ID执行对象ID 是相同的.
    另: 一个流程 流程实例 processInstance(PROC_INST_ID) 只有一个, 执行对象 execution(ID)可以存在多个. ↩︎

<think>好的,我现在需要分析用户提供的日志信息,并解释其中可能的问题。首先,用户之前询问过@DisallowConcurrentExecution的作用,现在给出了一段日志,其中显示同一任务在短时间内多次触发。我需要结合之前的回答,帮助用户理解这里发生了什么。 首先,观察日志的时间戳: 1. 第一条开始于08:55:00.228,结束于08:55:00.635,持续约407毫秒。 2. 第二条开始于08:55:01.016,结束于08:55:01.618,持续约602毫秒。 虽然任务执行时间都不超过1秒,但两次任务几乎同时启动,间隔只有不到1秒(第一次结束于00.635,第二次开始于01.016,间隔约381毫秒)。这说明任务被快速连续触发,可能存在并发执行的情况。 根据用户之前的讨论,Quartz的@DisallowConcurrentExecution注解可以防止同一JobDetail的并发执行。但日志中的任务在Worker-8和Worker-3两个不同的线程执行,可能意味着没有使用该注解,导致多个实例同时运行。需要检查代码中是否添加了该注解。 另外,调度间隔可能设置得太短,比如配置了每秒触发一次,或者Cron表达式有误。例如,如果调度设置为每秒钟执行一次,即使任务执行时间很短,也可能在极短时间内被多次触发。需要检查Quartz的触发器配置,确认调度间隔是否正确。 还要考虑是否有多个触发器绑定到同一个任务,导致任务被多次触发。例如,可能不小心配置了多个触发器,每个触发器都以高频率调用同一个Job,从而导致日志中的现象。 总结可能的原因: 1. 未添加@DisallowConcurrentExecution,导致任务并发执行。 2. 调度间隔设置过短(例如每秒一次),导致任务被频繁触发。 3. 存在多个触发器同时触发同一任务。 解决方案建议: 1.任务类上添加@DisallowConcurrentExecution注解,确保同一任务串行执行。 2. 检查并调整触发器配置,确保调度间隔合理,避免过短的触发间隔。 3. 检查是否有重复的触发器配置,确保任务只被预期的触发器调用。 需要提醒用户检查代码中的注解和配置,确认是否存在上述问题,并根据需要进行调整。</think>### 日志分析与问题定位 您提供的日志显示了 **“更新网络板控制器状态”任务在极短时间内被多次触发**(间隔约 1 秒),结合您之前对 `@DisallowConcurrentExecution` 的提问,以下是详细分析: --- #### 一、日志关键点提取 | 时间戳 | 线程名称 | 操作类型 | 任务耗时 | |-----------------------|---------------------|----------|--------------| | `2025-04-02 08:55:00.228` | `MyScheduler_Worker-8` | **开始** | 约 407 毫秒 | | `2025-04-02 08:55:00.635` | `MyScheduler_Worker-8` | **结束** | | | `2025-04-02 08:55:01.016` | `MyScheduler_Worker-3` | **开始** | 约 602 毫秒 | | `2025-04-02 08:55:01.618` | `MyScheduler_Worker-3` | **结束** | | **核心异常**: - 两次任务启动间隔仅 **381 毫秒**(第一次结束于 `00.635`,第二次开始于 `01.016`),远小于任务执行时间(数百毫秒)。 - 任务由不同线程(`Worker-8` 和 `Worker-3`)执行,表明 **并发触发**。 --- #### 二、原因分析 1. **未添加 `@DisallowConcurrentExecution` 注解** - **默认行为**:Quartz 允许同一 `JobDetail` 的多个实例并发执行- **日志表现**:任务未完成时,新线程立即启动另一个实例(即使任务执行时间短)。 2. **触发器(Trigger)配置问题** - **可能配置**:Cron 表达式或 `fixedRate` 间隔设置过短(例如每秒触发)。 - **示例错误配置**: ```java // 错误示例:每1秒触发一次 Trigger trigger = newTrigger() .withSchedule(simpleSchedule() .withIntervalInSeconds(1) .repeatForever()) .build(); ``` 3. **多触发器绑定同一任务** - 若存在多个触发器(如误配置重复触发器),会导致任务被多次触发。 --- #### 三、解决方案 1. **添加并发控制注解** 在任务类上添加 `@DisallowConcurrentExecution`,强制串行执行: ```java @DisallowConcurrentExecution public class GetNetworkControlJob implements Job { @Override public void execute(JobExecutionContext context) { // 更新网络板控制器状态逻辑 } } ``` 2. **检查触发器配置** - **Cron 表达式**:确保间隔合理(如 `0/5 * * * * ?` 表示每5秒一次)。 - **SimpleTrigger**:调整 `withIntervalInMilliseconds` 为合理值(如 5000 毫秒)。 3. **排查重复触发器** 检查 Quartz 的 `qrtz_triggers` 表或调度器配置,确保无冗余触发器。 --- #### 四、验证与优化 1. **修改后日志预期** - 任务结束 → 等待间隔 → 触发下一次执行(无并发线程)。 2. **性能监控** -任务执行时间波动较大,建议增加超时控制或异步处理,避免阻塞后续任务--- #### 五、总结 您的日志表明任务因 **缺少并发控制注解** 或 **触发器配置过密** 导致异常高频触发。通过添加 `@DisallowConcurrentExecution` 并调整触发器间隔,可有效解决该问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值