嵌入式Hazelcast执行器服务线程运行时如何阻止JVM关闭?
核心原因
Hazelcast默认的IExecutorService使用守护线程执行任务,JVM退出时不会等待守护线程完成,且JUnit等测试框架默认跑完测试用例主线程就会触发退出流程,连带关闭Hazelcast实例,最终导致未完成的任务被强制中断。
可用解决方案
方案1:等待Future返回结果
提交任务后拿到返回的Future对象,调用get()方法阻塞主线程,直到任务执行完成,也可传入超时时间避免无限等待:
IExecutorService executor = hazelcastInstance.getExecutorService("my-executor"); Future<?> future = executor.submit(new MyTask()); // 阻塞等待任务执行完成 future.get(30, TimeUnit.SECONDS);
如果提交多个任务,可以把所有Future存入集合,遍历逐个调用get()即可。
方案2:配置执行器线程为非守护类型
在Hazelcast配置中自定义执行器的线程工厂,将线程设置为非守护类型,JVM会等待所有非守护线程结束后再退出:
Config config = new Config(); ExecutorConfig executorConfig = config.getExecutorConfig("my-executor"); executorConfig.setThreadFactory(new ThreadFactory() { private final AtomicInteger counter = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName("hazelcast-executor-" + counter.incrementAndGet()); // 设置为非守护线程 t.setDaemon(false); return t; } }); HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
方案3:使用CountDownLatch同步
如果任务不需要返回结果,可使用CountDownLatch做同步控制,任务执行完成后计数减1,测试主线程等待计数归零后再结束:
// N为提交的任务总数 CountDownLatch latch = new CountDownLatch(1); executor.submit(() -> { try { // 业务执行逻辑 } finally { latch.countDown(); } }); // 最多等待30秒 latch.await(30, TimeUnit.SECONDS);
方案4:测试生命周期钩子中增加等待逻辑
如果使用JUnit等测试框架,可在资源销毁的钩子方法中增加等待逻辑,确认所有任务执行完成后再关闭Hazelcast实例:
@After public void tearDown() { IExecutorService executor = hazelcastInstance.getExecutorService("my-executor"); executor.shutdown(); // 等待执行器处理完所有已提交任务,最多等待60秒 executor.awaitTermination(60, TimeUnit.SECONDS); hazelcastInstance.shutdown(); }
注意:调用
shutdown()后执行器会停止接收新任务,但会继续处理完所有已排队的任务,配合awaitTermination可以保证任务全部执行完成再关闭实例。
内容的提问来源于stack exchange,提问作者Katha patel
相关产品推荐
相关产品推荐

