How do servlets work? Understanding instantiation, sessions, shared variables, and multithreading in a multi-user environment
Suppose I have a web server that hosts multiple servlets. For information passing among these servlets, I’m using session and instance variables.
When two or more users send requests to this server, what happens to the session variables? Are they common for all users or different for each user? If they’re different, how does the server differentiate between different users?
Additionally, if n users are accessing a particular servlet, is the servlet instantiated only once when the first user accesses it, or is it instantiated separately for each user? In other words, what happens to the instance variables in a multi-user scenario?
Java servlets are designed for efficiency by sharing a single instance across multiple user requests, with session variables providing user-specific data storage while instance variables are shared across all users, requiring careful thread safety considerations.
Contents
- Servlet Instantiation and Lifecycle
- Session Variables and User Differentiation
- Instance Variables and Multithreading Challenges
- Best Practices for Multi-User Servlet Applications
Servlet Instantiation and Lifecycle
In Java servlets, only one instance is created per servlet class, not per user. When the first user requests a servlet, the servlet container (like Tomcat or Jetty) instantiates that servlet once and reuses the same instance for all subsequent requests to that servlet. This design choice is made for performance reasons - creating and destroying servlet instances for every request would be prohibitively expensive.
The servlet lifecycle follows these key stages:
-
Loading and Instantiation: The servlet container loads the servlet class and creates a single instance when the first request arrives or during server startup.
-
Initialization: The
init()method is called once, providing the servlet with configuration information through theServletConfigobject. -
Request Handling: The
service()method (ordoGet(),doPost(), etc.) is called for each request, potentially simultaneously by multiple threads. -
Destruction: The
destroy()method is called once when the servlet is being unloaded, allowing for cleanup.
As explained by BalusC, “Servlets and filters are shared among all requests. That’s the nice thing of Java, it’s multithreaded and different threads (read: HTTP requests) can make use of the same instance.”
Session Variables and User Differentiation
Session variables are different for each user and are stored in HttpSession objects. Each user receives a unique session when they first interact with the web application, and this session persists across multiple requests from the same user.
The server differentiates between users through the following mechanism:
-
Session Creation: When a user first accesses the application, the servlet container creates a unique
HttpSessionobject for that user. -
Session ID Assignment: Each session is assigned a unique identifier (typically a long alphanumeric string called
JSESSIONID). -
Session Tracking: The session ID is maintained using one of these methods:
- Cookies: The most common approach, where the session ID is stored in a browser cookie
- URL Rewriting: Appending the session ID to URLs as a query parameter
- Hidden Fields: Storing session IDs in hidden form fields
As Decodejava.com explains, “This HttpSession object helps in storing a user information or a session data, to maintain its session and differentiate it from the other users.”
Here’s how you typically work with sessions in a servlet:
// Get or create a session for the current user
HttpSession session = request.getSession();
// Store user-specific data in the session
session.setAttribute("username", "john_doe");
session.setAttribute("userRole", "admin");
// Retrieve user-specific data from the session
String username = (String) session.getAttribute("username");
String userRole = (String) session.getAttribute("userRole");
The session exists for a configurable timeout period (typically 30 minutes) and survives even when different requests are made by the same user, allowing the application to maintain state across page navigation.
Instance Variables and Multithreading Challenges
Since only one servlet instance is shared among all users, instance variables present significant thread safety challenges in a multi-user environment. When multiple users access the same servlet simultaneously, they all share the same instance variables stored in the servlet’s heap memory.
This creates several problems:
-
Data Corruption: One user’s request might modify an instance variable while another user’s request is reading it, leading to inconsistent or corrupted data.
-
Race Conditions: Multiple threads might attempt to modify the same instance variable simultaneously, resulting in unpredictable behavior.
-
User Data Mixing: Data from one user might accidentally leak to another user through shared instance variables.
As InfoWorld points out, “The problem here is Thread-A has written to the instanceVar and is not expecting that value to change unless Thread-A explicitly does so. Unfortunately Thread-B is thinking the same thing regarding itself; the only problem is they share the same variable.”
Here’s an example of the problem:
public class UnsafeServlet extends HttpServlet {
private String currentUser; // Instance variable - NOT THREAD SAFE!
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
currentUser = username; // Problem: This affects all other users!
// Process request using currentUser...
}
}
In this example, if User A sets currentUser to “alice” and User B simultaneously sets it to “bob”, the value becomes unpredictable due to race conditions.
Thread Safety Solutions
To make servlets thread-safe, consider these approaches:
-
Avoid Instance Variables for Request-Specific Data: Store request-specific data in local variables or in the request/session scope.
-
Use Synchronization: If you must use instance variables, synchronize access carefully:
javapublic class ThreadSafeServlet extends HttpServlet { private String sharedData; protected void doGet(HttpServletRequest request, HttpServletResponse response) { synchronized(this) { // Lock on servlet instance // Modify or read instance variables here } } } -
Use Thread-Local Variables: For data that needs to be instance-scoped but thread-isolated:
javapublic class ThreadLocalServlet extends HttpServlet { private static final ThreadLocal<String> currentUser = new ThreadLocal<>(); protected void doGet(HttpServletRequest request, HttpServletResponse response) { String username = request.getParameter("username"); currentUser.set(username); // Only visible to current thread try { // Process request using currentUser.get() } finally { currentUser.remove(); // Clean up } } } -
Use Immutable Objects: If possible, design your servlet to work with immutable data structures.
As Wideskills.com recommends, “We recommend to synchronize the block where your code modifies the instance variables instead of synchronizing complete method for the sake of performance.”
Best Practices for Multi-User Servlet Applications
Session Management Best Practices
-
Use Sessions for User-Specific Data: Store user information, preferences, and request-specific data in
HttpSessionobjects rather than instance variables. -
Set Appropriate Session Timeouts: Configure session timeouts that balance security and user experience. Typical values range from 15-30 minutes.
-
Invalidate Sessions After Login: When a user logs in, invalidate any existing session and create a new one to prevent session fixation attacks:
javaHttpSession oldSession = request.getSession(false); if (oldSession != null) { oldSession.invalidate(); } HttpSession newSession = request.getSession(true); -
Use Session Attributes Wisely: Store only necessary data in sessions and remove attributes when they’re no longer needed.
Servlet Design Best Practices
-
Make Servlets Stateless: Design servlets to be stateless as much as possible, relying on sessions for state management.
-
Avoid Instance Variables: Use local variables, request attributes, or session attributes instead of instance variables for request-specific data.
-
Use Thread-Safe Collections: If you must use shared collections, use thread-safe implementations like
ConcurrentHashMap. -
Implement Proper Cleanup: Clean up resources in the
destroy()method and use try-with-resources for automatic resource management.
Example of a Well-Designed Servlet
public class UserProfileServlet extends HttpServlet {
// No instance variables - completely thread safe!
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// Get user's session (creates new session if needed)
HttpSession session = request.getSession();
// Get user-specific data from session
User user = (User) session.getAttribute("user");
if (user == null) {
// Handle unauthenticated user
response.sendRedirect("login.jsp");
return;
}
// Use local variables for request processing
String action = request.getParameter("action");
String userId = user.getId();
// Process request
if ("view".equals(action)) {
// Process view profile request
UserProfile profile = UserProfileService.getProfile(userId);
request.setAttribute("profile", profile);
request.getRequestDispatcher("/profile.jsp").forward(request, response);
}
// ... other actions
}
}
This example demonstrates a thread-safe servlet that:
- Uses no instance variables
- Relies on sessions for user identification
- Uses local variables for request processing
- Properly handles user authentication
- Separates concerns between servlet logic and view rendering
Following these practices will ensure your servlet applications work correctly in multi-user environments while maintaining performance and security.
Sources
- Oracle Documentation - Handling Threading Issues
- Wideskills - Concurrency in Servlets
- InfoWorld - Write Thread-Safe Servlets
- Stack Overflow - Difference between Servlet Instance and Thread
- Stack Overflow - Why Instance Variable in Servlet is Not Thread-Safe
- BalusC - Servlet Lifecycle and Multithreading
- Server2Client - Java Servlet Multithreading
- DigitalOcean - Session Management in Java
- CodeJava - How to Use Session in Java Web Application
- Decodejava - Session Management by HttpSession
Conclusion
Understanding servlet behavior in multi-user environments is crucial for building robust web applications. The key takeaways are:
- Servlets are instantiated once per class, not per user, making instance variables shared across all users
- Session variables are user-specific and stored in
HttpSessionobjects with unique session IDs - Thread safety is critical when using instance variables, requiring careful synchronization or alternative approaches
- Best practices include avoiding instance variables for request-specific data, using sessions for user state, and designing thread-safe servlets
By following these principles and implementing proper session management and thread safety measures, you can ensure your servlet applications work correctly and efficiently in multi-user environments while maintaining data integrity and security.