我已经定义了一个bean,它需要在@PostConstruct生命周期阶段(启动期间)进行一些繁重的处理。
就目前而言,我在处理循环的每次迭代中向执行者服务提交一个新的Callable。我在成员变量中保留了从这些提交中返回的Future对象的列表。
@Component
@Scope("singleton")
public class StartupManager implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private ExecutorService executorService;
private final Map<Class<?>, Optional<Action>> actionMappings = new ConcurrentHashMap<>();
private final List<Future> processingTasks = Collections.synchronizedList(new ArrayList<>());
@PostConstruct
public void init() throws ExecutionException, InterruptedException {
this.controllers.getHandlerMethods().entrySet().stream().forEach(handlerItem -> {
processingTasks.add(executorService.submit(() -> {
// processing
}));
});
}
}
这个bean实现了ApplicationListener接口,因此它可以侦听ContextRefreshedEvent,它允许我检测应用程序何时完成启动。我使用这个处理程序循环遍历Futures列表并调用阻塞get方法,该方法确保在应用程序继续之前已经发生了所有处理。
@Override
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {
for(Future task : this.processingTasks) {
try {
task.get();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e.getMessage());
}
}
}
我的第一个问题...是否将actionMapping流更改为parallelStream,以实现与向执行程序服务提交任务相同的功能?有没有办法可以将现有的执行程序服务传递到并行流中,以便使用我为bean定义的线程池大小?
其次..作为处理的一部分..将读取actionMappings地图并将条目放入其中。这足以使这个Map成为ConcurrentHashMap以使其在这种情况下的线程安全吗?
其次是实现ApplicationListener接口并监听ContextRefreshedEvent是检测应用程序何时启动的最佳方法,因此通过阻止来强制完成未处理的任务?或者这可以用另一种方式完成吗?
感谢。
答案 0 :(得分:1)
关于使用parallelStream()
:不,这正是使用此方法的主要缺点。它应该仅在线程池大小无关紧要时使用,因此我认为基于ExecutorService
的方法很好。
由于您正在使用Java 8,因此您也可以使用CompletableFuture.supplyAsync()
方法,该方法的重载需要Executor
。由于ExecutorService
延长Executor
,您可以将ExecutorService
传递给您,并且您已完成!
我认为ConcurrentHashMap
没问题。它确保了所有操作中的线程安全性,特别是在需要添加或修改条目时。
ContextRefreshedEvent
何时被解雇?根据Javadoc:
ApplicationContext
初始化或刷新时引发的事件。
并不保证您的onApplicationEvent()
方法只被调用一次,也就是说,当您的bean被正确初始化时,包括执行@PostConstruct
- 注释方法。
我建议您实施BeanPostProcessor
界面并将Future
- 检查逻辑放在postProcessAfterInitialization()
方法中。两个BeanPostProcessor
方法分别在InitializingBean.afterPropertiesSet()
方法(如果存在)之前和之后调用。
我希望这会有所帮助......
干杯,
杰夫