Servlets in Java: Instances, Sessions, and Multithreading — A Deep Dive

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why are servlets still scary, and how to deal with it?

\n\n

If you've ever browsed Stack Overflow looking for an answer to the question "How do servlets work?", you've likely stumbled upon a legendary thread with thousands of views. The question asked there time and again: "How many servlet instances are created? How do I share data between requests? And why is my variable acting crazy?"

\n\n

Let's be honest: servlets are not rocket science, but there are plenty of pitfalls. Especially when it comes to multithreading and shared variables. In this article, we won't just rehash the documentation. We'll take a real-life example, break it down piece by piece, and show you how to avoid common mistakes. Let's go!

\n\n

How a servlet enters memory: instantiation and lifecycle

\n\n

Let's start with the simple stuff: how many servlet instances are created? The answer: one. Yes, you heard that right. The servlet container (Tomcat, Jetty, etc.) creates exactly one instance of each servlet for the entire application. And this instance lives until the application is unloaded.

\n\n

Here's what a typical lifecycle looks like:

\n\n
    \n
  • init() — called once when the servlet is loaded. Here you initialize database connections, configurations, and so on.
  • \n
  • service() — called for each incoming request. This is where processing happens.
  • \n
  • destroy() — called before the servlet is unloaded. Clean up resources.
  • \n
\n\n

The key point: service() (and its derivatives doGet, doPost) is executed in a separate thread for each request. That means the same servlet instance handles hundreds of requests simultaneously. And this is where things get interesting.

\n\n

Example: one servlet, many threads

\n\n
@WebServlet("/counter")\npublic class CounterServlet extends HttpServlet {\n    private int counter = 0;  // shared variable!\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) \n            throws ServletException, IOException {\n        counter++;\n        resp.getWriter().write("Counter: " + counter);\n    }\n}
\n\n

If you run this code and send 10 parallel requests, you will not get the numbers 1 through 10 in order. Instead, you'll see something like: 2, 5, 3, 8, 1... Or counter might even skip values. Why? Because counter++ is not an atomic operation. Two threads can read the same value, increment it, and write it back, losing one increment.

\n\n

Conclusion: servlet instance fields are shared variables. They are visible to all threads. And if you don't synchronize access to them, you'll get a race condition.

\n\n

Sessions: how not to mix up users

\n\n

Now imagine: you want to store data unique to each user (shopping cart, login, settings). For this, Java Servlets provide HttpSession. A session is created for each new user and lives until they close the browser or the timeout expires.

\n\n

A session is not shared memory. Each user gets their own session instance. But inside the session, you can store any objects, and they will be accessible only to that user.

\n\n

Example: working with a session

\n\n
@WebServlet("/session-demo")\npublic class SessionServlet extends HttpServlet {\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) \n            throws ServletException, IOException {\n        HttpSession session = req.getSession();\n        Integer visitCount = (Integer) session.getAttribute("visitCount");\n        if (visitCount == null) {\n            visitCount = 0;\n                

Blogs

Book Recommendations