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. Caffe 激励层(Activation)分析

    Caffe_Activation 一般来说,激励层的输入输出尺寸一致,为非线性函数,完成非线性映射,从而能够拟合更为复杂的函数表达式激励层都派生于NeuronLayer: class XXXlayer ...

  2. Activiti BPMN 2.0 designer eclipse插件安装

    官方网是这样说的: https://www.activiti.org/userguide/index.html#springSpringBoot The following installation ...

  3. shell-3.bash的基本功能:通配符和其他特殊字符

    1. 2.

  4. 用MyBatis进行数据库的增删改查

    前提是MyBatis环境部署好了,参考地址: https://www.cnblogs.com/package-java/p/10316536.html 为了方便演示,我提前在数据库插入了数据方便查询 ...

  5. 运维派 企业面试题6 防dos攻击

    Linux运维必会的实战编程笔试题(19题) 企业实战题6:请用至少两种方法实现! 写一个脚本解决DOS攻击生产案例 提示:根据web日志或者或者网络连接数,监控当某个IP并发连接数或者短时内PV达到 ...

  6. (2016北京集训十)【xsy1528】azelso - 概率期望dp

    北京集训的题都是好题啊~~(于是我爆0了) 注意到一个重要的性质就是期望是线性的,也就是说每一段的期望步数可以直接加起来,那么dp求出每一段的期望就行了... 设$f_i$表示从$i$出发不回到$i$ ...

  7. iOS技术栈-Swift版

    在网上没找到基于Swift的技术栈介绍,或者技术已经不适用,所以自己画了一个,后面的博客中会重新整理基于Swfit的UI控件介绍和常用第三方库地图

  8. Lock-less buffer management scheme for telecommunication network applications

    A buffer management mechanism in a multi-core processor for use on a modem in a telecommunications n ...

  9. 没用私服,自己安装下本地jar包,制作坐标

    没用私服的话,自己安装下本地jar包吧.执行下面命令即可. -Dfile 代表jar存在路径.mvn install:install-file -DgroupId=alipay -DartifactI ...

  10. 怎样在windows7上使用snmp命令

    原文地址:http://wenboxz.com/archives/window7-use-snmp-command.html/