项目退出,项目里起的线程池是否能安全结束?

@JFinal

这个疑问(担心)一直存在,现在实际项目里要用到了,心里不踏实,还是希望波总能指导一下。

问题是这样的。JFinal+undertow项目,我做了一个插件,这个插件很简单,就是在start里创建了一个线程池,起了几个线程从redis里通过lpop取id,然后把数据库里根据这个id读出来数据,整理计算后再导入到solr里。

如果这个线程池里的线程正在计算,这时候中断执行整个项目,线程池里的线程会完成整个计算过程嘛?

我的担心是,由于lpop是从redis里直接弹出数据,redis里就没这个数据了,如果计算没完成,这个数据没处理完,就会导致丢失数据。

希望波总给与指导!

评论区

xiaoyi_e

2020-09-16 14:56

与中断方式有关,直接Kill掉的话,再多程序设计也没用;
常规途径关闭(如tomcat undeploy/shutdown的话),可以在onStop方法做清理;
与线程池方案也有关,如果是daemon模式,线程池会直接停止;如果不是,可能会一直占用资源,你的程序并不会终止(此时会出现undeploy不掉或tomcat shutdown失败的情况)

zzutligang

2020-09-17 10:10

退出不会使用kill -9 方式。直接kill。JFinal会发起onStop回调。所有插件的onstop方法都会被调到。我自己写了一个插件,在插件里创建一个线程池。在插件里onstop里控制线程池shutdown,并且给每个线程设置标志位,线程里的run方法根据标志位决定是否继续循环。这样,就可以保证程序退出的时候,线程里的线程肯定能运行完。实际测试了一下,目前看还算可以。感谢@xiaoyi_e

JFinal

2020-09-20 16:59

线程的关闭其实与 jfinal 无关,纯属 java 的知识范畴

在 java 知识体系中,如果 jvm 关闭的话,线程如果正在执行,并且是 "非daemon" 线程,jvm 会等待这个线程执行完以后再关闭

如果是 daemon 线程,jvm 在关闭的时候会立即关闭,不等待没有执行完的线程

就 jfinal 内部启动的线程来说,都确保了是 daemon 线程,所以,你只需要关心自己开启的线程是什么类型的即可

JFinal

2020-09-20 17:00

补充一下,如果用 kill 杀掉进程,使用 -9 参数,无论是不是 daemon 线程,都强制关闭 JVM,这个是操作系统层面的行为,是 JVM 自己也无法控制的

zzutligang

2020-09-21 12:14

@JFinal 感谢波总的科普。我目前是做了一个线程管理插件。在onstop里把所有我起的线程设置stop标志,线程的run函数里while循环每次都判断这个标志,false就退出run,线程也就结束了。然后线程池等待所有线程执行完。再退出。按这个逻辑,目前测试看是可以控制住不丢数据的。当然,如果进程被-9kill掉,谁也没办法了。

JFinal

2020-09-21 17:11

@zzutligang 如果线程能确保在有限的时间内执行完任务,jvm 退出时顶多是多等待一段时间退出 jvm, 但有些同学碰到这种情况以为是 jvm (tomcat、undertow、jetty) 无法关闭


最后,线程的管理可以使用 java 面成的 API,极度方便,例如我在项目中的发送模板消息的线程调度:

static ExecutorService executor = Executors.newFixedThreadPool(nThreads);

public sendTemplateMsg() {
executor.execute(() -> {TemplateKit.send(...)});
}

注意,上面的 executor.execute 方法中可以传入 Runnable、Thread ,也可以直接使用 lambda 传入一段代码,如 TemplateKit.send(...);

zzutligang

2020-09-21 18:24

@JFinal,是的,我就是这么做的,使用jdk的线程池ExecutorService来控制的。我能保证线程里run函数每次循环执行,只会执行很少的点时间,一般不会超过1秒,其实就是往redis里的list压数据,和从redis的list里pop数据。这都会很快的。每次run的时候都去检查stop标志是否设置成true,如果为true,就停止run里的while,这样线程也就执行完了。从理论上来说,这种方法应该是可以保证数据不会丢失的。