16. Servlets Architecture & Container Lifecycle

Servlet Lifecycle, Concurrency & Request Pipeline

Master the Servlet lifecycle pipeline (init, service, destroy), multithreaded single-instance concurrency hazards, and request/response abstractions.

Servlet Lifecycle, Concurrency & Request Pipeline

Understanding how Tomcat initializes, reuses, and dispatches requests to Servlets is essential for preventing concurrency bugs and resource leaks in enterprise Java applications.


1. The Servlet Lifecycle Pipeline

The Servlet lifecycle is strictly managed by the Servlet Container through four core stages:

HTTP GET /hello (First Request) Constructor Invocation init(ServletConfig) service(request, response) Route to doGet() Response Output Stream HTTP 200 OK Response HTTP GET /hello (Second Request) service(request, response) HTTP 200 OK Response destroy() Phase 2: Request Execution (Repeated) Phase 3: Subsequent Requests (Reuses Instance) Phase 4: Container Shutdown (Once) Browser Client Tomcat Container HelloServlet Instance

2. One Servlet Object, Many Requests (Concurrency Trap)

By default, Tomcat instantiates only ONE instance of a Servlet class to handle all concurrent incoming requests across multiple threads.

Concurrent Thread 1 (User A) ──┐
Concurrent Thread 2 (User B) ──┼──► [ SINGLE SERVLET INSTANCE ]
Concurrent Thread 3 (User C) ──┘

🔴 Dangerous Pattern: Shared Mutable Instance Variables

// UNSAFE: Instance variables are shared across ALL concurrent user threads!
public class CounterServlet extends HttpServlet {

    private int requestCounter = 0; // SHARED MUTABLE STATE

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        requestCounter++; // RACE CONDITION! Thread-unsafe state mutation
    }
}

[!WARNING] Concurrency Hazard: Do NOT store request-specific state in Servlet instance fields. Because one Servlet instance serves thousands of concurrent requests, instance fields create high-severity race conditions and cross-user data leakage.

✅ Safe Pattern: Local Variables inside Handlers

public class SafeServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // SAFE: Method-local variable allocated on the thread stack
        String userId = req.getParameter("id");
        processUser(userId);
    }
}

3. service() Method & HTTP Handler Routing

The primary request entry point invoked by Tomcat is service():

// Conceptual routing inside HttpServlet.service()
@Override
public void service(ServletRequest req, ServletResponse res) {
    HttpServletRequest request = (HttpServletRequest) req;
    String method = request.getMethod();

    if ("GET".equals(method)) {
        doGet(request, (HttpServletResponse) res);
    } else if ("POST".equals(method)) {
        doPost(request, (HttpServletResponse) res);
    } else if ("PUT".equals(method)) {
        doPut(request, (HttpServletResponse) res);
    } else if ("DELETE".equals(method)) {
        doDelete(request, (HttpServletResponse) res);
    }
}

4. Lifecycle Method Summary Matrix

Lifecycle MethodExecution FrequencyPrimary ResponsibilityRequest Specific?
ConstructorOnce per Servlet instanceMemory allocation❌ No
init()Once after instantiationOne-time resource setup (DB connections, caches)❌ No
service()Every incoming requestExamines HTTP verb & dispatches handler✅ Yes
doGet() / doPost()Every matching requestBusiness logic execution for specific HTTP verb✅ Yes
destroy()Once on application shutdownCleanup connection pools & flush buffers❌ No

❓ Knowledge Check

Knowledge Check

If 1,000 users concurrently send requests to a Servlet, how many instances of that Servlet class does Tomcat create by default?

Knowledge Check

Why are instance variables inside a Servlet considered dangerous?

On this page