The short answer is:
👉 Yes, you absolutely should worry about thread safety when working with servlets.
Let’s break this down very clearly:
🧠 Why?
- In a servlet container (like Tomcat, Jetty, etc.),
only one instance of your servlet class is created by default. - Many threads (one per incoming request) are sent to the same servlet instance to handle requests at the same time.
- This means multiple threads can execute your
doGet()
,doPost()
, etc. simultaneously!
⚡ If you have shared mutable state in your servlet (like instance variables),
you risk:
- Race conditions
- Inconsistent data
- Deadlocks
- Security issues
🎯 Very Important:
Servlet Object | Shared between Threads? | Thread Safe? |
---|---|---|
Servlet instance | ✅ Yes (one instance for all threads) | ❌ No (you must protect shared data) |
HttpServletRequest | ❌ No (each thread gets its own request) | ✅ Yes (no need to protect) |
HttpServletResponse | ❌ No (each thread gets its own response) | ✅ Yes (no need to protect) |
✅ Request and Response objects are thread-safe (separate for each request).
❌ Servlet instance fields are shared (dangerous if modified).
🛠️ Bad Example (NOT thread-safe!)
public class UnsafeServlet extends HttpServlet {
private int counter = 0; // SHARED mutable field!
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
counter++; // NOT thread-safe!
resp.getWriter().println("Counter: " + counter);
}
}
Problem:
- Two users might increase
counter
at the same time, causing wrong values (lost updates).
✅ Correct Approaches
1. Avoid using instance variables
Simply use only local variables inside doGet()
, doPost()
:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int counter = 0; // local to this request
counter++;
resp.getWriter().println("Counter: " + counter);
}
2. Use thread-safe objects (if you must share state)
- Use
AtomicInteger
,ConcurrentHashMap
, etc.
private AtomicInteger counter = new AtomicInteger(0);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int currentCount = counter.incrementAndGet();
resp.getWriter().println("Counter: " + currentCount);
}
✅ AtomicInteger
safely increments across threads without race conditions.
3. Synchronize carefully (but try to avoid it)
You can synchronize manually:
private int counter = 0;
@Override
protected synchronized void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
counter++;
resp.getWriter().println("Counter: " + counter);
}
⚡ But beware:
- Synchronization can cause performance bottlenecks under heavy load.
- Better to design without shared mutable state whenever possible.
🛡️ Practical Rule of Thumb:
Question | Best Practice |
---|---|
Do you need to store data per request? | ✅ Use local variables. |
Do you need to share data across users? | ✅ Use thread-safe collections / atomic variables / application scope (carefully). |
Do you need locks? | ❌ Only if absolutely necessary, otherwise redesign. |
🔥 Real-Life Examples of Servlet Thread Safety Issues:
- Incorrect login counters
- Random failures in shopping carts
- Inconsistent session management
- Mixed-up responses between users
Almost always caused by improperly handled shared variables inside servlets.
🛠️ Quick Recap
Aspect | Servlet Behavior |
---|---|
Servlet instance per app? | ✅ Yes, only one |
Multiple threads per servlet? | ✅ Yes |
Safe to use instance fields? | ❌ No, unless they are thread-safe |
Local variables inside doGet() ? | ✅ Yes, perfectly safe |
Use Atomic* , Concurrent* types for shared data? | ✅ Yes, best practice |