Spring-statemachine版本:当前最新的1.2.3.RELEASE版本

这几天一直被Action是串行执行搞得很郁闷,写了一个demo专门用来测试:

public static void main(String[] args) throws Throwable {
StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder(); StaticListableBeanFactory beanFactory = new StaticListableBeanFactory(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(30);
executor.setKeepAliveSeconds(30000);
executor.setMaxPoolSize(30);
executor.setQueueCapacity(3000);
executor.initialize(); builder.configureConfiguration()
.withConfiguration()
.beanFactory(beanFactory)
.taskExecutor(executor)
.taskScheduler(new ConcurrentTaskScheduler())
.listener(new StateMachineListenerAdapter<String, String>() {
@Override
public void stateEntered(State<String, String> state) {
String id = state == null ? null : state.getId();
System.out.println("entered " + id);
} @Override
public void stateExited(State<String, String> state) {
String id = state == null ? null : state.getId();
System.out.println("exited " + id);
}
}); StateMachineStateConfigurer<String, String> smsConfigurer = builder.configureStates();
smsConfigurer.withStates()
.initial("READY")
.fork("FORK")
.state("TASKS")
.join("JOIN")
.choice("CHOICE")
.state("ERROR")
.and()
.withStates()
.parent("TASKS")
.initial("T1")
.end("T1E")
.and()
.withStates()
.parent("TASKS")
.initial("T2")
.end("T2E"); StateMachineTransitionConfigurer<String, String> smtConfigurer = builder.configureTransitions();
smtConfigurer.withExternal()
.source("READY").target("FORK")
.and()
.withFork()
.source("FORK").target("TASKS")
.and()
.withJoin()
.source("TASKS").target("JOIN")
.and()
.withExternal()
.source("T1").target("T1E")
.action(getAction())
.and()
.withExternal()
.source("T2").target("T2E")
.action(getAction())
.and()
.withExternal()
.source("JOIN").target("CHOICE")
.and()
.withChoice()
.source("CHOICE")
.first("ERROR", c -> true)
.last("READY"); StateMachine<String, String> stateMachine = builder.build(); stateMachine.start();
} public static Action<String, String> getAction() {
return c -> {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>..");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
;
}
System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<..");
};
}
}

打出来的日志是

entered READY
exited READY
entered TASKS
entered T1
>>>>>>>>>>>>>>>>>>>>>>>>>>..
entered T2
<<<<<<<<<<<<<<<<<<<<<<<<<<..
exited T1
entered T1E
>>>>>>>>>>>>>>>>>>>>>>>>>>..
<<<<<<<<<<<<<<<<<<<<<<<<<<..
exited T2
entered T2E
exited TASKS
entered ERROR

如果是并发执行的话,应该像是这样:

>>>>>>>>>>>>>>>>>>>>>>>>>>..
>>>>>>>>>>>>>>>>>>>>>>>>>>..
<<<<<<<<<<<<<<<<<<<<<<<<<<..
<<<<<<<<<<<<<<<<<<<<<<<<<<..

然后趁着周末debug一发,跟着代码一步步走,发现org.springframework.statemachine.config.AbstractStateMachineFactory类中有这段代码:

// 从所有region的transition和当前region的state中抽取出当前region的transition
//
// in代表所有的transition(包括父状态机),stateDatas代表当前region(子状态机)的状态
private Collection<TransitionData<S, E>> resolveTransitionData(Collection<TransitionData<S, E>> in, Collection<StateData<S, E>> stateDatas) {
ArrayList<TransitionData<S, E>> out = new ArrayList<TransitionData<S,E>>(); Collection<Object> states = new ArrayList<Object>();
for (StateData<S, E> stateData : stateDatas) {
states.add(stateData.getParent()); // 抽出父状态机的状态
} for (TransitionData<S, E> transitionData : in) {
S state = transitionData.getState(); // 从下一行代码可以推理得出此处的getState得到的是父状态机的状态
if (state != null && states.contains(state)) { // 核心代码,如果父状态机的状态包含state,就加入到out去当作子状态机的transition,最后返回子状态机的transition集合
out.add(transitionData);
}
} return out;
}

看到了吧,我当时猜测构建transition时还有一个state方法用来标识父状态机的状态,于是到withExternal()后面尝试看看有没有一个叫做state()的方法,结果还真有!

然后代码改成这样:

smtConfigurer
.withExternal()
.source("READY").target("FORK")
.and()
.withFork()
.source("FORK").target("TASKS")
.and()
.withJoin()
.source("TASKS").target("JOIN")
.and()
.withExternal()
.state("TASKS") // 增加本行设置父状态机的状态
.source("T1").target("T1E")
.action(getAction())
.and()
.withExternal()
.state("TASKS") // 增加本行设置父状态机的状态
.source("T2").target("T2E")
.action(getAction())
.and()
.withExternal()
.source("JOIN").target("CHOICE")
.and()
.withChoice()
.source("CHOICE")
.first("ERROR", c -> true)
.last("READY");

结果日志终于并发执行了,好开心啊~

entered READY
exited READY
entered TASKS
entered T2
entered T1
>>>>>>>>>>>>>>>>>>>>>>>>>>..
>>>>>>>>>>>>>>>>>>>>>>>>>>..
<<<<<<<<<<<<<<<<<<<<<<<<<<..
<<<<<<<<<<<<<<<<<<<<<<<<<<..
exited T2
exited T1
entered T2E
entered T1E
exited TASKS
entered SUCCESS

关于设置transition的state,官方的reference并没有相关的说明,所以初次使用spring-statemachine做并发任务状态管理的话,基本上都会遇到这个问题。

Github issue: https://github.com/spring-projects/spring-statemachine/issues/336

Spring-statemachine Action不能并发执行的问题的更多相关文章

  1. spring in action 4th --- quick start

    读spring in action. 环境搭建 quick-start依赖注入 面向切面 1.环境搭建 jdk1.8 gradle 2.12 Intelij idea 2016.2.1 1.1创建一个 ...

  2. Spring in action记录

    最近一段时间重新学习了一遍SPRING,现在对这些笔记整理一下,一来算是对之前的学习有一个交代,二来当是重新学习一次,三来可以留下备份 这次学习中以SPRING IN ACTION 4这学习资料,整书 ...

  3. quartz中设置Job不并发执行

    使用quartz框架可以完成定时任务处理即Job,比如有时候我们设置1个Job每隔5分钟执行1次,后来会发现当前Job启动的时候上一个Job还没有运行结束,这显然不是我们期望的,此时可以设置quart ...

  4. Spring in Action 4th 学习笔记 之 AOP

    前提:本文中的AOP仅限于Spring AOP. 先说说为什么需要AOP 最简单的一个例子就是日志记录,如果想记录一些方法的执行情况,最笨的办法就是修改每一个需要记录的方法.但这,真的很笨... 好的 ...

  5. spring quartz使用多线程并发“陷阱”

    定义一个job:ranJob,设置每秒执行一次,设置不允许覆盖并发执行 <bean id="rankJob" class="com.chinacache.www.l ...

  6. 使用Spring StateMachine框架实现状态机

    spring statemachine刚出来不久,但是对于一些企业的大型应用的使用还是十分有借鉴意义的. 最近使用了下这个,感觉还是挺好的. 下面举个例子来说下吧: 创建一个Spring Boot的基 ...

  7. 【说解】在shell中通过mkfifo创建命名管道来控制多个进程并发执行

    背景: 工作中有两个异地机房需要传数据,数据全名很规范,在某个目录下命名为统一的前缀加上编号.如/path/from/file.{1..100}.而机房间的专线对单个scp进程的传输速度是有限制的,比 ...

  8. 1、Spring In Action 4th笔记(1)

    Spring In Action 4th笔记(1) 2016-12-28 1.Spring是一个框架,致力于减轻JEE的开发,它有4个特点: 1.1 基于POJO(Plain Ordinary Jav ...

  9. SSIS Design3:并发执行

    1,利用优先约束来并发处理数据,Data Flow Task 和 Data Flow Task 1 是并发执行的,而 Data Flow Task2 必须等到 Data Flow Task 和 Dat ...

随机推荐

  1. [转] CentOS 7 为firewalld添加开放端口及相关资料

    转自http://www.cnblogs.com/hubing/p/6058932.html 1.运行.停止.禁用firewalld 启动:# systemctl start  firewalld 查 ...

  2. 使用TensorFlow编写常用模块的Python代码示例

    将数据转化成tfrecords的形式 import os import tensorflow as tf import numpy as np import matplotlib.image as m ...

  3. react-native之文件上传下载

    目录 文件上传 1.文件选择 2.文件上传 1.FormData对象包装 2.上传示例 文件下载 最近react-native项目上需要做文件上传下载的功能,由于才接触react-native不久,好 ...

  4. java 导出百万数据到excel

    @RequestMapping("export") public void write(HttpServletRequest request,HttpServletResponse ...

  5. vue非父子组件间传参问题

    最近在使用vue进行开发,遇到了组件之间传参的问题,此处主要是针对非父子组件之间的传参问题进行总结,方法如下:一.如果两个组件用友共同的父组件,即 FatherComponent.vue代码 < ...

  6. 紫书 例题 11-1 UVa 12219 (表达式树)

    这道题看了刘汝佳的代码真的是天秀, 很值得学习. 具体看代码 #include<cstdio> #include<iostream> #include<cctype> ...

  7. centos安全配置

    http://www.dedecms.com/knowledge/servers/linux-bsd/2012/0819/8506.html 引言: 我们必须明白:最小的权限+最少的服务=最大的安全 ...

  8. 在VS2013中配置QT5 win7_64

    转自 在VS2013中配置QT5 win7_64 环境: win x64 + vs2013+QT5+vs_addin 下面示例正确配置QT以及VS2013 + QT Addin开发环境: 下载VS20 ...

  9. Android OpenGL ES(七)----理解纹理与纹理过滤

    1.理解纹理 OpenGL中的纹理能够用来表示图像.照片,甚至由一个数学算法生成的分形数据.每一个二维的纹理都由很多小的纹理元素组成.它们是小块的数据,类似于我们前面讨论过的片段和像素.要使用纹理,最 ...

  10. Asterisk[1]

    Asterisk[1]是一款GPLv2协议下的开源电话应用平台.简单来说,Asterisk是一个server应用.可以完毕发起电话呼叫.接受电话呼叫.对电话呼叫进行定制处理. 1.2.1 通道驱动 a ...