I have a class with two #Scheduled methods as follows.
public class JobExecutor {
private static final DelayQueue<Job> JOB_QUEUE = new DelayQueue<>();
#Scheduled
public void run() {
Job job = JOB_QUEUE.take();
}
#Scheduled
public void fillQueue {
JOB_QUEUE.add(.....);
}
}
I am using a thread pool with 20 threads. Now I am using a DelayQueue which is a blocking queue in the run method. Is there a possibility that all the 20 threads gets stuck reading the queue(when it is empty) and the fillQueue method never gets executed?
No, because DelayQueue returns null if there are no elements for poll
If have had a look into the code. If you configure the scheduler pool by xml
<task:executor id="executor" pool-size="10"/>
then the pool-size will become the corePoolSize of the builded ScheduledThreadPoolExecutor
ThreadPoolTaskScheduler.createExecutor line 146:
new ScheduledThreadPoolExecutor(poolSize, threadFactory, rejectedExecutionHandler);
and the maximumPoolSize will be Integer.MAX_VALUE !
Therefor the answer is: NO, it is just a core pool size and more threads will been created if they are requested. So your code will not block.
Related
I have a project which executes multiple scheduled method at start up.
I remarked that after scheduled methods are executed, the opened threads do not close, but remain in a 'parking' state.
Is this a normal behavior ?
Aren't the threads suppose to close after method is executed ? (Because keeping multiple threads open just slows down the application and consumes more RAM.)
Here are my code configurations:
#EnableScheduling
#Configuration
#ConditionalOnProperty(name = "scheduling.enabled", matchIfMissing = true)
public class SchedulingConfiguration implements SchedulingConfigurer {
}
Here is an example of method called in service:
#Scheduled(cron = "0 0 4 * * *")
protected void updateExchangeRates() {
if (enablePostConstruct) {
countryService.updateCountryExchangeRates();
}
}
I would like to run the scheduled methods asynchronously, with a max thread pool consumed between 10-15 threads. And after execution, the thread to close and reopen in case it got to the point when it needs to be executed again.
Can you guide me please how this can be achieved ?
I tried to implement SchedulingConfigurer and perform executorService.shutdown(), but it did not work.
You could use a method annotated with #PreDestroy to invoke executorService.shutdown(). I wouldn't bother about the Parking State, you probably want those threads to be ready for the next invocation, so not really harmful that they are parked.
Nothing wrong with the code.
I currently have a scheduled task within my Spring application.
However a two parts of this logic is severely time-consuming and I am wondering if there would be a way to make these two parts asynchronous so that it does not interfere with the time of the logic being executed.
The logic that I need to execute as follows.
#Scheduled(fixedDelay = 10000)
public void startAuction() throws Exception {
List<SchGoodsAuctionStartListRes> list = schedulerService.schGoodsAuctionStartList();
for (SchGoodsAuctionStartListRes item : list) {
schedulerService.schGoodsAuctionStart(item);
// 1st time consuming block that needs async
PushInfo pushInfo = pushMapper.pushGoodsSeller(item.getGoodsIdx());
pushInfo.setTitle("Start");
pushInfo.setBody("[" + pushInfo.getBrand() + "] started.");
pushInfo.setPushGrp("001");
pushInfo.setPushCode("003");
fcmPushUtil.sendPush(pushInfo);
// 2nd time consuming block that needs async
List<PushInfo> pushInfos = pushMapper.pushGoodsAuctionAll(item.getIdx());
for (PushInfo pushInfoItem : pushInfos) {
pushInfoItem.setTitle("\uD83D\uDD14 open");
pushInfoItem.setBody("[" + pushInfo.getBrand() + "] started. \uD83D\uDC5C");
pushInfoItem.setPushGrp("002");
pushInfoItem.setPushCode("008");
fcmPushUtil.sendPush(pushInfoItem);
}
}
}
From my understanding, a scheduler already is executing logic asynchronously, and I wonder if there would be any way of making those two blocks asynchronous so that it does not cause delays when executing this logic.
Any sort of advice or feedback would be deeply appreciated!
Thank you in advance!
There are several approaches that you could take here.
Configuring Thread Pool executor for Spring's scheduled tasks
By default Spring uses single thread executor to execute scheduled tasks, which means that even if you have multiple #Scheduled tasks or another execution for a task triggers before the previous one is completed, they will all have to wait in the queue.
You can configure your own executor to be used by Spring Scheduling. Take a look at the documentation of #EnableScheduling, it is pretty exhaustive on the subject.
To configure ExecutorService to be used for scheduled tasks it is enough to define a bean:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(8);
threadPoolTaskScheduler.setThreadNamePrefix("task-scheduler");
return threadPoolTaskScheduler;
}
Additionally, if you use Spring Boot, you can use properties file:
spring.task.scheduling.pool.size=8
Executing scheduled tasks asynchronously
To execute scheduled tasks asynchronously you can use Spring's #Async annotation (and make sure to #EnableAsync somewhere in your configuration. That will make your tasks to be executed on a background thread, freeing the scheduling thread.
#EnableAsync
public class ScheduledAsyncTask {
#Async
#Scheduled(fixedRate = 10000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
// your task logic ...
}
}
Offload expensive parts of your tasks to a different executor
Finally, you could use a separate ExecutorService and run expensive parts of your tasks using that executor instead of the one used for task scheduling. This will keep the time needed to complete the execution on the thread used by Spring to schedule tasks to a minimum, allowing it to start next executions.
public class ScheduledAsyncTask implements DisposableBean {
private final ExecutorService executorService = Executors.newFixedThreadPool(4);
#Scheduled(fixedRate = 10000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
executorService.submit(() -> {
// Expensive calculations ...
});
}
#Override
public void destroy() {
executorService.shutdown();
}
}
In my application I have a single java Executor, can it create issues if I create multiple Scheduler from the same instance of Executor, having something like:
public class MyServiceController {
#Autowired
private Executor mainExecutor;
public Object something() {
return Flux.from(somethingElse())
.publishOn(Schedulers.fromExecutor(mainExecutor))
.toFuture()
}
}
(Or having multiple classes implementing this pattern, all of them having the same instance of mainExecutor)
it should be fine in both cases, the same Executor will back all Scheduler.Worker spawned by each Scheduler you create from it, and the same is true if it is an ExecutorService (albeit with a different Scheduler implementation for the wrapper).
For clarity, I'd still consider making the Scheduler a singleton next to the Executor.
I have defined a bean which needs to do some heavy processing during the #PostConstruct lifecycle phase (during start up).
As it stands, I submit a new Callable to an executor service with each iteration of the processing loop. I keep a list of the Future objects returned from these submissions in a member variable.
#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
}));
});
}
}
This same bean implements the ApplicationListener interface, so it can listen for a ContextRefreshedEvent which allows me to detect when the application has finished starting up. I use this handler to loop through the list of Futures and invoke the blocking get method which ensures that all of the processing has occurred before the application continues.
#Override
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {
for(Future task : this.processingTasks) {
try {
task.get();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e.getMessage());
}
}
}
My first question... Is changing the actionMapping stream to a parallelStream going to achieve the same thing as submitting a task to the executor service? Is there a way I can pass an existing executor service into a parallel stream for it to use the thread pool size i've defined for the bean?
Secondly.. As part of the processing.. The actionMappings map is read and entries are put in there. It is sufficient enough to make this Map a ConcurrentHashMap to make it thread safe in this scenario?
And secondly is implementing the ApplicationListener interface and listening for the ContextRefreshedEvent the best way to detect when the application has startedup and therefore force complete the un-processed tasks by blocking? Or can this be done another way?
Thanks.
About using parallelStream(): No, and this is precisely the main drawback of using this method. It should be used only when the thread pool size doesn't matter, so I think your ExecutorService-based approach is fine.
Since you are working with Java 8, you could as well use the CompletableFuture.supplyAsync() method, which has an overload that takes an Executor. Since ExecutorService extends Executor, you can pass it your ExecutorService and you're done!
I think a ConcurrentHashMap is fine. It ensures thread safety in all its operations, especially when comes the time to add or modify entries.
When is a ContextRefreshedEvent fired? According to the Javadoc:
Event raised when an ApplicationContext gets initialized or refreshed.
which doesn't guarantee your onApplicationEvent() method is to be called once and only once, that is, when your bean is properly initialized, which includes execution of the #PostConstruct-annotated method.
I suggest you implement the BeanPostProcessor interface and put your Future-checkup logic in the postProcessAfterInitialization() method. The two BeanPostProcessormethods are called before and after the InitializingBean.afterPropertiesSet() method (if present), respectively.
I hope this will be helpful...
Cheers,
Jeff
i am trying to execute a task at a fixed rate using the #Scheduled annotation in java spring. however, it seems that by default spring will not execute a fixedRate task at a fixed rate if the task is slower than the rate. is there some setting i can add to my spring configuration to change this behavior?
example:
#Service
public class MyTask{
#Scheduled(fixedRate = 1000)
public void doIt(){
// this sometimes takes >1000ms, in which case the next execution is late
...
}
}
i have a work-around, but it seems less than ideal. basically, i just replace the default single-thread executor with a thread pool, then i have a scheduled method call an async method since the #Async annotation allows concurrent executions:
#Service
public class MyTask{
#Async
public void doIt(){
// this sometimes takes >1000ms, but the next execution is on time
...
}
}
#Service
public class MyTaskScheduler{
...
#Scheduled(fixedRate = 1000)
public void doIt(){
myTask.doIt();
}
}
#Configuration
#EnableScheduling
#EnableAsync
public class MySpringJavaConfig{
#Bean(destroyMethod = "shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(5);
}
}
boring details of my real-world scenario: in my production code i have a task that takes between 10ms and 10 minutes depending on the current workload. ideally, i would like to capture a new thread from the pool every 1000ms so that the number of concurrent threads increases with the workload. obviously i have an upper limit on threads in place (among other controls) to keep things from getting out of hand.
The TaskScheduler API (which backs some of the Spring Scheduling behavior) seems to be defined to prevent the behavior you are requesting
Schedule the given Runnable, invoking it at the specified execution
time and subsequently with the given period.
Parameters
period the interval between successive executions of the task (in milliseconds)
subsequently and successive seem to indicate that the next execution will only occur after the current execution is complete.
What's more, ScheduledExecutorService#scheduleAtFixedRate(..) (which the built-in TaskScheduler implementations use) also says
If any execution of this task takes longer than its period, then
subsequent executions may start late, but will not concurrently
execute.
So there's another layer of the implementation that prevents the behavior you want.
A possible solution, and one I don't recommend since the API doesn't seem to be built around it, is to define and provide your own TaskScheduler that does run the task concurrently. Look into #EnableScheduling and SchedulingConfigurer for how to register a TaskScheduler.
the best solution i have found so far is to simply use a delegate to make the method calls async. this is only preferable because it allows me to declare the schedule in the same class as the method which does the work:
#Service
public class AsyncRunner {
#Async
public void run(Runnable runnable) {
runnable.run();
}
}
#Service
public class MyTask{
...
#Scheduled(fixedRate = 1000)
public void scheduleIt(){
asyncRunner.run(this::doIt);
}
public void doIt(){
// this sometimes takes >1000ms, but the next execution is on time
...
}
}