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.
Related
If i want a method to repeat async, can i use #Scheduled and #Async together ?
#Async
#Scheduled(fixedDelay = x)
public void doSomethingEveryXMinuteAsync() {
// action
}
or is there another standard way to achive this ?
There is no need to use #Async. Just use fixedRate attribute of #Scheduled instead of fixedDelay. Spring will make another invocation on the method after the given time regardless of any call is already being processed.
UPDATE:
Apparently fixedRate attribute does not enforce a scheduled method to be called asynchronously and increasing pool size of scheduler task executor only enables asynchronous execution of independent #Scheduled methods. Even putting #Async on the method does not make it work as OP has asked.
ScheduledAnnotationBeanPostProcessor just creates a Runnable from the #Scheduled method and does not create any pointcut as #Async method processor would. ScheduledThreadPoolExecutor waits until Runnable#run() is finished and sets the next execution time using the start time and the fixed rate. So if the method call takes more time than the scheduled time, the next task is triggered right after the previous call is finished.
An easy solution would be extracting the actual method into another class as a #Async method and calling this method from the #Scheduled method.
Implement SchedulingConfigurer and override configureTasks method.
Define poolsize more than one, It will work as you are expecting.
You should configure implementing SchedulingConfigurer:
#Configuration
#EnableScheduling
public class ScheduledConfiguration implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar)
{
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("your-scheduler-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
With this code you achieve parallel execution of your method which is annotated with #Scheduled.
You may also set the property:
spring.task.scheduling.pool.size
#Async and #Scheduled method shouldn't be in a same class.
follow the manual and add AsyncConfigurerhttps://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html
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.
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
...
}
}
my application is running on tomee and I have the ejb timer to trigger the timeout method every two minutes. The timer triggered the timeout method first time and is still running when the timer tried to trigger the same method for second time. And it threw the following exception..
javax.ejb.ConcurrentAccessTimeoutException: Unable to get write lock on 'timeout' method for: com.abc.xyz
at org.apache.openejb.core.singleton.SingletonContainer.aquireLock(SingletonContainer.java:298)
at org.apache.openejb.core.singleton.SingletonContainer._invoke(SingletonContainer.java:217)
at org.apache.openejb.core.singleton.SingletonContainer.invoke(SingletonContainer.java:197)
at org.apache.openejb.core.timer.EjbTimerServiceImpl.ejbTimeout(EjbTimerServiceImpl.java:769)
at org.apache.openejb.core.timer.EjbTimeoutJob.execute(EjbTimeoutJob.java:39)
at org.quartz.core.JobRunShell.run(JobRunShell.java:207)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:560)
All my log is filled up with the same stacktrace and it continues to occur until I stop the server..
Can we make the timerservice not to trigger the method if it is already running?
or is there a way to timeout the first call before it is triggered again..
Thanks,
Is your timed EJB a singleton bean?
By default singletons use container managed concurrency with write locks that guarantee exclusive access for all methods.
The openejb.xml configures the AccessTimeout for a singleton EJB. After that timeout the exception you have seen will be thrown. Please see here as well: http://tomee.apache.org/singleton-beans.html
Solutions might be:
Use a stateless session bean as the timer bean
Define a read lock on the timer method
Don't use a repeating timer but schedule the next execution of your timer at the end of the current execution.
If you want to avoid running multiple times in parallel, but also want to avoid that the scheduled runs queue up, then I have another proposal.
This way I let schedules "skip", if the previous one is still running:
#Singleton
#Startup
#ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Example
{
private final AtomicBoolean alreadyRunning = new AtomicBoolean(false);
#Schedule(minute = "*", hour="*", persistent = false)
public void doWork()
{
if (alreadyRunning.getAndSet(true)) return;
try
{
// ... your code
}
finally
{
alreadyRunning.set(false);
}
}
}
I have created a bean of a class with default (Singleton) scope. Within the class I have a method which is scheduled to be run every hour.
public class TaskService implements InitializingBean {
#Scheduled(cron="0 0 */1 * * ?")
public void hourlyReportTask()
{
... code here ...
}
public void performAllTasks()
{
hourlyReportTask();
...
...
}
}
My application config looks something like this,
<bean id="reportService"
class="com.tasks.TaskService" />
I am assuming the Thread running the scheduled task will be using the same TaskService bean since its created in singleton scope. What shall happen if the application is currently running hourlyReportTask() and the Spring container kicks off a background scheduled thread to run hourlyReportTask() at the same time. Will it wait for the to get access of the TaskService instance?
The exact same instance is used by both your application and the scheduling service. There is no synchronization so the scheduling service may run that method while your application invokes it.
Pretty much the same way as you would have injected TaskService in something that can be accessed by multiple threads at the same time and those threads call that method concurrently.
There's no black magic behind #Scheduled: it invokes your method the same way as you would manually. If that method is not thread-safe you need to fallback on regular synchronization mechanism in Java (for instance by adding the synchronized keyword to your method declaration).
Spring Singleton, does not mean what you expect from Design Patterns Singleton. In Spring, Singleton means that a bean only has created only one instance (without meaning that another cannot be created) and that instance is used whenever Spring needs that type.
In your case your hourlyReportTask() method would execute twice.