java线程池实例项目,java线程池应用实例
00-1010简介1。审查流程引擎2的关键代码。SpringBean3的异步执行。如何辨别异步跳簧4?模拟流程引擎数据中心5。新线程池6。测试7。总结。
00-1010线程池面试中,面试官除了问ThreadPoolExecutor的底层源代码,还喜欢问你在实际工作中有没有使用过ThreadPoolExecutor。我们在并发集合类的文章《场景集合:并发 List、Map 的应用场景》里讲过一个简单的流程引擎。没看过的可以回去看看。
在流程引擎的基础上,本章使用ThreadPoolExecutor和线程池来实现SpringBean的异步执行。
00-1010《场景集合:并发 List、Map 的应用场景》本文中执行SpringBean的流程引擎的核心代码是:
//批量执行Spring Bean Private Void Stage Invoke(String flow name,Stage Enum Stage,flow content){ listdomainability Bean domain abilities=flow center . flow map . getordefault(flow name,Maps.newHashMap())。get(阶段);if(collection utils . isempty(domain abilities)){ ThrowNewRuntimeException(找不到此进程对应的域行为 flowName);} for(domain ability bean domain ability 3360 domain abilities){//执行spring bean domain ability . invoke(content);}}参数是flowName(流程名)、stage(阶段)、content(上下文),其中很多Spring bean会在stage中执行,正在执行的Spring bean的代码是domainAbility.invoke(内容)。
00-1010从上面的代码我们可以看出,所有的Spring beans都是串行执行的,效率很低。我们在实际业务中发现,有些Spring beans可以异步执行,既能完成业务请求,又能减少业务处理的rt。对于这个需求,我们有两个条件性的想法:
您需要一个新线程来异步执行SpringBean。您可以使用Runable或Callable;请求的数量是巨大的,所以我们不能每次请求来的时候都启动一个线程。我们应该让线程池管理异步执行的线程。
所以我们决定使用线程池来满足这个需求。
00-1010我们的SpringBean都实现了接口DomainAbilityBean。该接口定义如下:
接口域能力Bean {/* * *域行为的方法入口*/流内容调用(流内容内容);}从接口定义来看,没有预留位置来标识SpringBean应该同步执行还是异步执行。这时,我们可以采取注释的形式,我们将创建一个新的注释。只要SpringBean上有这个注释,就意味着SpringBean应该异步执行,否则应该同步执行。新创建的注释如下:
/* * *异步SpringBean执行注意事项*如果Spring Bean需要异步执行,则标记注释*/@Target(ElementType。TYPE)//表示注释应该标记在类@ retention(retention policy . runtime)@ documented public @ interfaceasynccomponent { }上然后,我们创建了两个新的Spring bean,在其中一个上标记了异步注释,并打印出执行Spring bean的线程的名称,如下图所示:
两个SpringBean:图中实现的bean one and和BeanTwo,其中BeanTwo用AsyncComponent注释,表示BeanTwo应该异步执行,两个spring bean都打印出执行线程的名称。
/p>
4、mock 流程引擎数据中心
《场景集合:并发 List、Map 的应用场景》一文中,我们说可以从数据库中加载出流程引擎需要的数据,此时我们 mock 一下,mock 的代码如下:
@Componentpublic class FlowCenter { /** * flowMap 是共享变量,方便访问 */ public static final Map<String, Map<StageEnum, List<DomainAbilityBean>>> flowMap = Maps.newConcurrentMap(); /** * PostConstruct 注解的意思就是 * 在容器启动成功之后,初始化 flowMap */ @PostConstruct public void init() { // 初始化 flowMap mock Map<StageEnum, List<DomainAbilityBean>> stageMap = flowMap.getOrDefault("flow1",Maps.newConcurrentMap()); for (StageEnum value : StageEnum.values()) { List<DomainAbilityBean> domainAbilitys = stageMap.getOrDefault(value, Lists.newCopyOnWriteArrayList()); if(CollectionUtils.isEmpty(domainAbilitys)){ domainAbilitys.addAll(ImmutableList.of( ApplicationContextHelper.getBean(BeanOne.class), ApplicationContextHelper.getBean(BeanTwo.class) )); stageMap.put(value,domainAbilitys); } } flowMap.put("flow1",stageMap); // 打印出加载完成之后的数据结果 log.info("init success,flowMap is {}", JSON.toJSONString(flowMap)); }}
5、新建线程池
在以上操作完成之后,只剩下最后一步了,之前我们执行 SpringBean 时,是这行代码:domainAbility.invoke(content);
现在我们需要区分 SpringBean 是否是异步的,如果是异步的,丢到线程池中去执行,如果是同步的,仍然使用原来的方法进行执行,于是我们把这些逻辑封装到一个工具类中,工具类如下:
public class ComponentExecutor {// 我们新建了一个线程池 private static ExecutorService executor = new ThreadPoolExecutor(15, 15, 365L, TimeUnit.DAYS, new LinkedBlockingQueue<>());// 如果 SpringBean 上有 AsyncComponent 注解,表示该 SpringBean 需要异步执行,就丢到线程池中去 public static final void run(DomainAbilityBean component, FlowContent content) { // 判断类上是否有 AsyncComponent 注解 if (AnnotationUtils.isAnnotationPresent(AsyncComponent.class, AopUtils.getTargetClass(component))) { // 提交到线程池中 executor.submit(() -> { component.invoke(content); }); return; } // 同步 SpringBean 直接执行。 component.invoke(content); }}
我们把原来的执行代码替换成使用组件执行器执行,如下图:
6、测试
以上步骤完成之后,简单的流程引擎就已经完成了,我们简单地在项目启动的时候加上测试,代码如下:
更严谨的做法,是会写单元测试来测试流程引擎,为了快一点,我们直接在项目启动类上加上了测试代码。
运行之后的关键结果如下:
[main] demo.sixth.SynchronizedDemo: SynchronizedDemo init begin[main] demo.sixth.SynchronizedDemo: SynchronizedDemo init end[main] demo.three.flow.FlowCenter : init success,flowMap is {"flow1":{"PARAM_VALID":[{},{}],"AFTER_TRANSACTION":[{"$ref":"$.flow1.PARAM_VALID[0]"},{"$ref":"$.flow1.PARAM_VALID[1]"}],"BUSINESS_VALID":[{"$ref":"$.flow1.PARAM_VALID[0]"},{"$ref":"$.flow1.PARAM_VALID[1]"}],"IN_TRANSACTION":[{"$ref":"$.flow1.PARAM_VALID[0]"},{"$ref":"$.flow1.PARAM_VALID[1]"}]}}o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup[main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)[main] demo.DemoApplication : Started DemoApplication in 5.377 seconds (JVM running for 6.105)[main] demo.three.flow.BeanOne : BeanOne is run,thread name is main[main] demo.three.flow.BeanOne : BeanOne is run,thread name is main[pool-1-thread-1] demo.three.flow.BeanTwo : BeanTwo is run,thread name is pool-1-thread-1[main] demo.three.flow.BeanOne : BeanOne is run,thread name is main[pool-1-thread-2] demo.three.flow.BeanTwo : BeanTwo is run,thread name is pool-1-thread-2[pool-1-thread-3] demo.three.flow.BeanTwo : BeanTwo is run,thread name is pool-1-thread-3[main] demo.three.flow.BeanOne : BeanOne is run,thread name is main[pool-1-thread-4] demo.three.flow.BeanTwo : BeanTwo is run,thread name is pool-1-thread-4
从运行结果中,我们可以看到 BeanTwo 已经被多个不同的线程异步执行了。
7、总结
这是一个线程池在简单流程引擎上的运用实站,虽然这个流程引擎看起来比较简单,但在实际工作中,还是非常好用的,大家可以把代码拉下来,自己尝试一下,调试一下参数,比如当我新增 SpringBean 的时候,流程引擎的表现如何。
以上就是Java线程池流程编排运用实战源码的详细内容,更多关于Java线程池流程编排运用的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。