项目里有一个处理Job的工作流。
实体对应的Job有两个状态:一是实体的Job的状态,以下称为State,另一个是每个实体Job状态的内部状态,以下称为JobState。
其中,State的变迁为业务相关(DOWNLOADRUN, PROCESSRUN等),JobState为业务不相关(NEW -> INPROGRESS -> COMPLETED)。
主线程负责State的变迁。
用一个线程池Executors.newSingleThreadExecutor来实际处理Job,即负责JobState的变迁。
当线程池中的线程将JobState变为COMPLETED之后,主线程将State前挪,并将JobState重置为NEW,重新让线程池中的线程处理。
伪代码如下:
while(on){
List<? extends IJobEntity> jobEntities = listToDoIJobEntities();
for (IJobEntity jobEntity : jobEntities){
Enum nextState = jobEntity.getEntityNextState();
Enum newState = nextState;
if (nextState != null){ // TODO: probably need to add nextState != NULL in the sql query above
if (nextState.ordinal() == readyState.ordinal() - 1){
newState = readyState;
nextState = null;
dao.update(null, mapOf("state", newState, "nextState", nextState, "jobState", null), jobEntity.getEntityId());
}
else{
nextState = jobEntity.getJobNextState();
dao.update(null, mapOf("state", newState, "nextState", nextState, "jobState", JobState.NEW), jobEntity.getEntityId());
}
IJobEntity tmpJobEntity = getDao().get(null, jobEntity.getEntityId()).orElse(null);
}
}
jobEntities = listNewAndStartedJobEntities();
for (IJobEntity jobEntity : jobEntities){
CompletableFuture<JobResult> future = CompletableFuture.supplyAsync(() -> {
Enum nextState = jobEntity.getEntityNextState();
IJobEntity latestJobEntity= getDao().get(null, jobEntity.getEntityId()).orElse(null);
if (nextState != latestJobEntity.getEntityNextState()){
return null;
}
// perform the task and set JobState INPROGRESS
return getDoers().doJob(nextState, jobEntity.getEntityId());
}, jobExecutor);
future.thenAccept(jobResult -> {
if (jobResult == null){
return;
}
IJobEntity tmpJobEntity = getDao().get(currentUserHolder.get(),jobResult.getEntityId()).orElse(null);
Map<String, Object> data = new HashedMap();
// Set JobState COMPLETED
data.put("jobState", jobResult.getJobState());
dao.update(currentUserHolder.get(), data, jobResult.getEntityId());
});
}
}
附上newSingleThreadExecutor的背景知识:
ThreadExecutorPool的构造函数有多个参数
- corePoolSize: the number of threads to keep in the pool
- maximumPoolSize: the maximum number of threads to allow in the pool
- keepAliveTime: 当线程数量大于corePoolSize时,当一个线程无事可做时,超过一定的时间(keepAliveTime),这个线程就会被停掉。
- unit
- workQueue:类型为BlockingQueue
newSingleThreadExecutor创建的corePoolSize为1,maximumPoolSize为1,workQueue为unbounding的LinkedBlockingQueue,即无界队列,允许无限多排队。
同时launch多个Job(比如2个),称为Job1,Job2.
Job1和Job2同时进入NEW状态。
Job1被process为COMPLETED,准备进入下一状态;
Job2随后被process为COMPLETED,也准备进入下一状态。
问题来了,当Job2被处理结束后,日志显示,Job2又立马被设置成了INPROGRESS状态,重跑一遍INPROGRESS->COMPLETED。
跟踪后发现,当Job1在处理的同时,主线程仍然在listStartOrNewEntities,这个时候listStartOrNewEntities的结果只有Job2,所以Job2被不停的挂在线程池等待队列上等待被处理。
这也是为什么Job2在被处理结束状态进入COMPLETED的同时,立即被设置为INPROGRESS的原因。
这个问题的解决方案有几种:
方案1: 为JobState增加一个QUEUED的状态。创建CompletableFuture之前,如果状态为QUEUED,就不创建也就避免了排队;
方案2:将INPROGRESS状态设置这个动作,单拎出来,作为一个任务放在两个foreach之间。
方案3:在CompletableFuture的处理函数里面做判断,如果JobState已经为COMPLETED,不予处理直接返回。
其中,方案3,仅仅是跳过处理,仍然让Job进行了等待队列的排队,逻辑不正确,不可取。
只能从方案1和方案2中选择一种解决方案。
先说下背景,过去两年,在某渣外企,从一团乱麻开始,摸石头过河,完整经历组织敏捷转型,跟着两个敏捷团队一起初步成型。 我的角色是Scrum Master加Technical Leader。
先摆结论:
一切规定细节行为的教科书和敏捷教练,都是耍流氓!!
一切规定细节行为的教科书和敏捷教练,都是耍流氓!!
一切规定细节行为的教科书和敏捷教练,都是耍流氓!!
这几天稍微心情稍微烦闷了些。 便找了两位80后的代表人物的电影来看。
后会无期,韩寒导演生涯处女做。 小时代,郭敬明编剧和执导。
感觉两部片子蕴含的三观都是一致的。
后会无期流水线一般刻画了乡情、友情、爱情、亲情、兄弟情的后会无期。 韩寒这个80后代表人物,上海松江中学新概念作文大赛成长,当年曹文轩给了他很高的评价,《杯中窥人》等作品现在都仍然还记得起片段。 这个上海大城市长大的80后,在这部公路片的构思,和不同感情的刻画和表现上,确实展现了他的功底。
小时代相比就要肤浅一些。全篇堆砌生硬的段子和台词,刻画的是四个妹纸的友情、成长等等,不可避免的掺杂了拜金、物质等各种非主流和低速的价值观。但是也可以理解。郭敬明这个同样也是从新概念作文大赛中成长的娃,跟韩寒比,搓就搓在他生长在一个渣的三四线城市。不可避免地沉醉于上海这样的大城市的拜金、物质、装逼氛围之中。
这二位其实都是在通过自己的作品,找寻自己心底里缺失的东西。
顺便一提,小时代的那几首歌真心还是写得不错,旋律歌词都朗朗上口。
Android的应用程序开发有一个著名的Handler Leak问题。
在描述这个问题的原理和解决方案前,先摆一个java的基础知识:
在java中,非静态的内部类和匿名内部类会隐式地持有其外部类的引用。 静态的内部类不会持有其外部类的引用。
基础知识摆了后,下面是正式主题。
关于Android的Hanlder,官方文档有这样一段说明:
In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.
翻译:在Android中,Handler类应该是静态的,否则就可能会造成泄漏。应用程序的MessageQueue中排队的Message对象还保留着他们的目标Handler。如果这个Handler是一个内部类,那他的外部类也会被保留(原因见文头)。所以为了避免泄漏外部类,需要将Handler生命为静态内部类,并且让其持有其外部类的WeakReference。
导致泄漏的原因有两个:
这两个原因的共同作用造成了Android的Handler Leak.
要解决这个问题,就应该从这两个原因入手。
对于原因1,可以在Handler所在的组件生命周期结束前清除掉MessageQueue中发送给Handler的Message对象.
对于原因2,可以将Handler实现为静态内部类的方式,从而避免了对外部类的强引用。如果在Handler中需要使用外部类,则可以在Handler内部声明一个WeakReference引用到外部类的实例。