How to run a background task in a servlet based web application?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Background work in a servlet-based web application is a common need for tasks such as sending emails, processing uploads, or running scheduled maintenance. The challenge is doing it in a way that fits the servlet container’s lifecycle rather than fighting against it.
The safest rule is simple: do not create unmanaged ad hoc threads inside request handlers unless you fully understand the container consequences. Prefer container-managed executors or a clearly owned scheduler, and keep request handling separate from long-running background work.
Why Raw Threads Are a Bad Default
A naive approach is to start a thread directly from a servlet:
This looks easy, but it has real problems:
- the container does not manage the thread lifecycle
- redeploy or shutdown can leave work in an undefined state
- you have no structured error handling or resource control
- thread creation inside request code can scale badly
In other words, it works as a quick demo but is usually a poor production pattern.
Use a Managed Executor When Available
In a Jakarta EE or Java EE style environment, a managed executor is the preferred option because the container owns the threading resources.
Then a servlet can submit work without manually creating threads:
This is much better aligned with the application server model.
If You Control the Whole App, Use a Managed Scheduler Pattern
In simpler servlet deployments where a managed executor is not available, a controlled ScheduledExecutorService created at application startup is a more disciplined fallback than random per-request threads.
A servlet can retrieve the scheduler from the context and submit jobs to it. This is still app-managed, but at least the lifecycle is centralized and shutdown is handled.
Keep the Request Fast
A servlet request should usually do just enough to validate input, enqueue or submit the work, and return a response. Long-running business logic should stay out of the request thread.
That gives you better responsiveness and avoids tying request timeouts to background-processing time.
A good pattern is:
- validate request data
- submit background work
- return a job ID or acknowledgment
- expose a separate endpoint for status if needed
That model scales much better than blocking the request until the task finishes.
Consider External Job Systems for Important Work
If the background work is critical, long-running, or needs retries across restarts, a servlet-local executor may still not be enough. In that case, use a queue, scheduler, or dedicated worker system outside the servlet container.
Examples include:
- message queues
- dedicated worker services
- Quartz-style schedulers
- batch processing systems
The servlet app then becomes the place that accepts requests and submits jobs, not the place that owns every long-lived background execution detail.
Common Pitfalls
One common mistake is starting raw threads inside servlets and never shutting them down on redeploy. That can leak resources and create duplicate workers after application reloads.
Another issue is doing heavy work on the request thread and calling it “background” just because it was triggered by a request. If the HTTP thread is still blocked, it is not really background work.
It is also easy to ignore error handling. Background tasks still need logging, retry policy, and a clear ownership model.
Finally, do not assume all servlet containers manage custom threads safely. Container-managed concurrency and unmanaged Java threads are not equivalent.
Summary
- Avoid creating unmanaged ad hoc threads directly inside servlet request handlers.
- Prefer a container-managed executor when the platform provides one.
- If you must manage your own scheduler, create and shut it down through application lifecycle hooks.
- Keep HTTP requests short by submitting work rather than performing long background tasks inline.
- For important or durable jobs, consider an external queue or worker system instead of keeping everything inside the servlet container.

