ScheduledExecutorService is used in a bean constructor, which should run when spring boot starts.
#RestController
public class TestController {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
public TestController() {
service.schedule(() -> {
// task
}, 0, TimeUnit.SECONDS);
service.shutdown();
}
However, sometimes, the task is never called. And in debug mode, service.shutdown() gives service as:
active threads = 1, queued tasks = 0, completed tasks = 0
While this does not happen all the time. When it runs as I think it should, service object at the same line gives either queued tasks or completed tasks 1.
This only happens during junit test,
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest()
#AutoConfigureMockMvc
public class ScheduledExecutorServiceTest {
#Test
public void test() {
}
}
You may delay the controller thread to wait for the execution of the scheduled thread with a call to
scheduledFuture.get() or
scheduledFuture.get(long timeout, TimeUnit unit),
where
scheduledFuture = service.schedule(..)
before
service.shutdown().
You are scheduling the task to run in another thread but shutting down the scheduler service immediately. shutdown method initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted and it does not wait for previously submitted tasks to complete execution. It may happen that sometimes your scheduler service is already shut down before getting the task, hence it shows queued or completed task as 0.
Related
As a result of Spring integration MessageQueue without polling, I have a poller that consumes messages from a queue instantly, using a custom TaskScheduler:
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("resultProcessor-");
IntegrationFlows
.from("inbound")
.channel(MessageChannels.priority().get())
.bridge(bridge -> bridge
.taskScheduler(taskScheduler)
.poller(Pollers.fixedDelay(0).receiveTimeout(Long.MAX_VALUE)))
.fixedSubscriberChannel()
.route(inboundRouter())
.get()
Now I'd like to have multiple threads consuming concurrently, so I tried:
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("resultProcessor-");
scheduler.setPoolSize(4);
However, since in AbstractPollingEndpoint the task scheduler schedules a synchronous poller (it's a bit complicated), only 1 thread is created. If I set the TaskExecutor to anything but SyncTaskExecutor (default), I run into a flood of scheduled tasks (see Spring integration MessageQueue without polling).
How can I concurrently consume from a queue in Spring Integration? This seems pretty basic but I couldn't find a solution.
Instead of the queue I could use an ExecutorChannel, however, (AFAIK) I then lose queue features like priority, queue size, and metrics on which I rely.
See PollerSpec.taskExecutor():
/**
* Specify an {#link Executor} to perform the {#code pollingTask}.
* #param taskExecutor the {#link Executor} to use.
* #return the spec.
*/
public PollerSpec taskExecutor(Executor taskExecutor) {
This way after scheduling the task periodically according your taskScheduler and delay, the real task is performed on a thread from that provided executor. By default it indeed performs the task on a scheduler's thread.
UPDATE
I'm not sure if this meets your requirements, but this is only way to keep your queue logic and process whatever is pull in parallel:
.bridge(bridge -> bridge
.taskScheduler(taskScheduler)
.poller(Pollers.fixedDelay(0).receiveTimeout(Long.MAX_VALUE)))
.channel(channels -> channel.executor(threadPoolExecutor()))
.fixedSubscriberChannel()
I was able to solve it like this:
A single-threaded task scheduler that performs the polling
A thread pool executor with a synchronous queue
This way, the task scheduler can give each executor 1 task and blocks when no executor is free, thus not draining the source queue or spamming tasks.
#Bean
public IntegrationFlow extractTaskResultFlow() {
return IntegrationFlows
.from(ChannelNames.TASK_RESULT_QUEUE)
.bridge(bridge -> bridge
.taskScheduler(taskResultTaskScheduler())
.poller(Pollers
.fixedDelay(0)
.taskExecutor(taskResultExecutor())
.receiveTimeout(Long.MAX_VALUE)))
.handle(resultProcessor)
.channel(ChannelNames.TASK_FINALIZER_CHANNEL)
.get();
}
#Bean
public TaskExecutor taskResultExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, // corePoolSize
8, // maximumPoolSize
1L, // keepAliveTime
TimeUnit.MINUTES,
new SynchronousQueue<>(),
new CustomizableThreadFactory("resultProcessor-")
);
executor.setRejectedExecutionHandler(new CallerBlocksPolicy(Long.MAX_VALUE));
return new ErrorHandlingTaskExecutor(executor, errorHandler);
}
#Bean
public TaskScheduler taskResultTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("resultPoller-");
return scheduler;
}
(The initial example was copied from the linked question, this one now resembles my actual solution)
I am using ScheduledExecutorService and initialized it (ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(20);) through a singleton class so that I don't create new threads every time. I then schedule my task using schedule "executorService.schedule(new Runnable(), 20, TimeUnit.SECONDS);
I have 2 questions on this:
1. How do I shutdown a thread once its job is over. If I am trying to call a shutdown method after first execution I get java.util.concurrent.RejectedExecutionException error (as the main executor is shutdown).
2. How do I cancel a long running thread after some time? Let's say if a request is sent and a thread is stuck in the execution how should I cancel it after some time.
The best way to end your long running thread is to return from the Runnable#run function. Any attempts to interrupt or cancel may not always work.
Update: I am finding the problem with other threads too; they enter Scheduled state but never transition to Running. why?
My program has a Service that uses a Task to connect to a device through the serial port.
In other words,
public class ConnectService extends Service<String> {
protected Task createTask() {
return new ConnectTask();
}
class ConnectTask extends Task<ObservableList<String>> {
#Override
protected ObservableList<String> call() throws Exception {
...
connect();
...
}
}
}
If a previous call to connect to the device got hung, then I want to cancel the task/thread and start over afresh in this attempt.
In order to do this,
if (connectService.getState() != Worker.State.READY) {
connectService.cancel();
}
connectService.restart();
However in the debugger I am finding that if the state is SCHEDULED, then the above code sends it to CANCELLED. But restart() will not send it to READY - instead it goes back to SCHEDULED - and call() does not get executed!
This seems to contradict the docs here
A reusable Worker will transition from CANCELLED, SUCCEEDED or FAILED
back to READY.
I tried
if (connectService.getState() != Worker.State.READY) {
connectService.cancel();
connectService.reset();
}
connectService.start();
Now state goes back to READY, but call() is never executed!
You need to add Worker.State.SCHEDULED to the states excluded from your if condition, i.e.
if (connectService.getState() != Worker.State.READY &&
connectService.getState() != Worker.State.SCHEDULED) {
connectService.cancel();
}
connectService.restart();
This is because the Worker will always transition from READY to SCHEDULED before it enters the RUNNING state. From the docs
However even in cases where the Worker is executed immediately, the
Worker will temporarily enter the SCHEDULED state before entering the
RUNNING state. That is, the transition is always from READY to
SCHEDULED to RUNNING (unless of course the Worker in cancelled).
If for some reason your working is stuck in the SCHEDULED state, it's likely that the cancel and restart just returns it to the same stuck state.
Also, (not seeing the rest of your code, so this is just extrapolation) the idea of interrupting it when you catch it running on the assumption that it is hung seems shaky, since it could be working well but taking longer than expected, or it could be hanging every time you call it. If there's no test you can run within connectService to determine whether it's hung or not, then I guess you're stuck with something like this, but it feels problematic.
I'm working on a Java EE Web application.
I've developed a Scheduled Batch using #WebListener annotation:
#WebListener
public class BatchServlet implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
context = sce.getServletContext();
schedulerEngine = Executors.newSingleThreadScheduledExecutor();
schedulerEngine.scheduleAtFixedRate(new MyThread(), 0, 300, TimeUnit.SECONDS);
}
public class MyThread implements Runnable {
#Override
public void run() {
...... //my business logic
}
and it works correctly.
But, for my purpose, I need to force the MyThread() batch on demand, for example, when I received a particular request within a Servlet. Obviosly I can call the MyBatch thread separately, but I was wondering that there are some best practices to perform this operation.
There are a couple of options: create a regular thread pool (with multiple threads) and just put tasks on the executor queue whenever they come in.
If you need more fine-grained control, you can share something like a concurrent queue between the servlet and the one thread that you have. Let this thread read from the queue, it will block automatically when the queue is empty.
Then your servlet can put work on this queue, if the thread happened to be blocked on getting an item it will automatically wake up.
You might wanna look up the "producer-consumer" pattern. There is a lot of literature about this.
How do "modern" cron daemons internally schedule their jobs? Some cronds used to schedule a run every so often via at. So after a crontab is written out, does crond:
Parse the crontab for all future events and the sleep for the intervals?
Poll an aggregated crontab database every minute to determine if the current time matches the schedule pattern?
Other?
Thanks,
A few crickets heard in this question. Good 'ol RTFC with some discrete event simulation papers and Wikipedia:
http://en.wikipedia.org/wiki/Cron#Multi-user_capability
The algorithm used by this cron is as
follows:
On start-up, look for a file named .crontab in the home directories of
all account holders.
For each crontab file found, determine the next time in the future
that each command is to be run.
Place those commands on the Franta-Maly event list with their
corresponding time and their "five
field" time specifier.
Enter main loop:
Examine the task entry at the head of the queue, compute how far in the
future it is to be run.
Sleep for that period of time.
On awakening and after verifying the correct time, execute the task at
the head of the queue (in background)
with the privileges of the user who
created it.
Determine the next time in the future to run this command and place
it back on the event list at that time
I wrote a blog post describing it.
Quoting the relevant text from there:
We can have a finite thread-pool which will execute all the tasks by picking them up from a PriorityBlockingQueue (thread-safe heap) prioritized on job.nextExecutionTime().
Meaning that the top element of this heap will be always be the one that will fire the soonest.
We will be following the standard threadpool producer-consumer pattern.
We will have one thread which will be running in an infinite loop and submitting new jobs to the thread pool after consuming them from the queue.
Lets call it QueueConsumerThread:
void goToSleep(job, jobQueue){
jobQueue.push(job);
sleep(job.nextExecutionTime() - getCurrentTime());
}
void executeJob(job, jobQueue){
threadpool.submit(job); // async call
if (job.isRecurring()) {
job = job.copy().setNextExecutionTime(getCurrentTime() + job.getRecurringInterval());
jobQueue.add(job);
}
}
#Override
void run(){
while(true)
{
job = jobQueue.pop()
if(job.nextExecutionTime() > getCurrentTime()){
// Nothing to do
goToSleep(job, jobQueue)
}
else{
executeJob(job, jobQueue)
}
}
}
There will be one more thread which will be monitoring the crontab file for any new job additions and will push them to the queue.
Lets call it QueueProducerThread:
#Override
void run()
{
while(true)
{
newJob = getNewJobFromCrontabFile() // blocking call
jobQueue.push(newJob)
}
}
However, there is a problem with this:
Imagine that Thread1 is sleeping and will wake up after an hour.
Meanwhile a new task arrives which is supposed to run every minute.
This new task will not be able to start executing until an hour later.
To solve this problem, we can have ProducerThread wakeup ConsumerThread from its sleep forcefully whenever the new task has to run sooner than the front task in the queue:
#Override
void run()
{
while(true)
{
newJob = getNewJobFromCrontabFile() // blocking call
jobQueue.push(newJob)
if(newJob == jobQueue.peek())
{
// The new job is the one that will be scheduled next.
// So wakeup consumer thread so that it does not oversleep.
consumerThread.interrupt()
}
}
}
Note that this might not be how cron is implemented internally.
However, this is the most optimal solution that I can think of.
It requires no polling and all threads sleep until they need to do any work.