3.9. Thread SafetyIn a typical scenario, only one copy of any particular servlet or filter is loaded into the server's runtime at any given time. Each servlet might, however, be called upon to deal with multiple requests at the same time. This means that a servlet needs to be threadsafe. If a servlet doesn't use any class variables (that is, any variables with a scope broader than the service method itself), it is generally already threadsafe. If you are using any third-party libraries or extensions, make sure that those components are also threadsafe. However, a servlet that maintains persistent resources needs to make sure that nothing untoward happens to those resources. Imagine, for example, a servlet that maintains a bank balance using an int in memory.[*] If two servlets try to access the balance at the same time, you might get this sequence of events:
Obviously, this is incorrect behavior, particularly that last bit. We want the servlet to perform the necessary action for User 1, and then deal with User 2 (in this case, by giving him an insufficient funds message). We can do this by surrounding sections of code with synchronized blocks. While a particular synchronized block is executing, no other sections of code that are synchronized on the same object (usually the servlet or the resource being protected) can execute. For more information on thread safety and synchronization, see Java Threads by Scott Oaks and Henry Wong (O'Reilly). Example 3-6 implements the ATM display for the First Bank of Java. The doGet( ) method displays the current account balance and provides a small ATM control panel for making deposits and withdrawals, as shown in Figure 3-3.[]
Figure 3-3. The First Bank of Java ATM displayThe control panel uses a POST request to send the transaction back to the servlet, which performs the appropriate action and calls doGet( ) to redisplay the ATM screen with the updated balance. Example 3-6. An ATM servletimport javax.servlet.*; import javax.servlet.http.*; import java.util.*; import java.io.*; public class AtmServlet extends HttpServlet { Account act; public void init(ServletConfig conf) throws ServletException { super.init(conf); act = new Account( ); act.balance = 0; } public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); PrintWriter out = resp.getWriter( ); out.println("<html><body>"); out.println("<h2>First Bank of Java ATM</h2>"); out.println("Current Balance: <b>" + act.balance + "</b><br>"); out.println("<form method=post>"); out.println("Amount: <input type=text name=AMOUNT size=3><br>"); out.println("<input type=submit name=DEPOSIT value=\"Deposit\">"); out.println("<input type=submit name=WITHDRAW value=\"Withdraw\">"); out.println("</form>"); out.println("</body></html>"); } public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int amt=0; try { amt = Integer.parseInt(req.getParameter("AMOUNT")); } catch (NullPointerException e) { // No Amount parameter passed } catch (NumberFormatException e) { // Amount parameter was not a number } synchronized(act) { if((req.getParameter("WITHDRAW") != null) && (amt < act.balance)) act.balance = act.balance - amt; if((req.getParameter("DEPOSIT") != null) && (amt > 0)) act.balance = act.balance + amt; } // End synchronized block doGet(req, resp); // Show ATM screen } public void destroy( ) { // This is where we would save the balance to a file } class Account { public int balance; } } The doPost( ) method alters the account balance contained within an Account object act (since Account is so simple, we've defined it as an inner class). In order to prevent multiple requests from accessing the same account at once, any code that alters act is synchronized on act. This ensures that no other code can alter act while a synchronized section is running. The destroy( ) method is defined in the AtmServlet, but it contains no actual code. A real banking servlet would obviously want to write the account balance to disk before being unloaded. And if the servlet were using JDBC to store the balance in a database, it would also want to destroy all its database-related objects.
|