Page 3 of 5
<HTML>
<BODY>
<TABLE>
<TR>
<TD>
<IFRAME src="/theWebapp/SimpleServlet"
name="servlet1"
height="200%">
</IFRAME>
</TD>
</TR>
<TR>
<TD>
<IFRAME src="/theWebapp/SimpleServlet"
name="servlet2"
height="200%">
</IFRAME>
</TD>
</TR>
<TR>
<TD>
<IFRAME src="/theWebapp/SimpleServlet"
name="servlet3"
height="200%">
</IFRAME>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
Our code, which is a non-thread-safe servlet, generates the following output:
ThreadSafety.SimpleServlet@1694eca: Counter=0 Counter=2 Counter=4 Counter=6 Counter=9 Counter=11 Counter=13 Counter=15 Counter=17 Counter=19 ThreadSafety.SimpleServlet@1694eca: Counter=0 Counter=1 Counter=3 Counter=5 Counter=7 Counter=8 Counter=10 Counter=12 Counter=14 Counter=16 ThreadSafety.SimpleServlet@1694eca: Counter=18 Counter=20 Counter=22 Counter=23 Counter=24 Counter=25 Counter=26 Counter=27 Counter=28 Counter=29
As we can see in our output, we fail to get the results we desire. Notice the value printed from the this reference is duplicated. This is the servlet's memory address. It tells us that only one servlet is instantiated to service
all requests. The servlet tried its best to output sequential data, but because all threads share the memory allocated for
counter, we managed to step on our own toes. We can see that the values are not always sequential, which is bad! What if that variable
is being used to point at a user's private information? What if a user logs into their online banking system and on a particular
page, that user sees someone else's banking information? This problem can manifest itself in many ways, most of which are
difficult to identify, but the good news is that this problem is easily remedied. So let's take a look at our options.
I have always said that the best way to fix problems is to avoid them all together; in our case, this approach is best. When discussing thread safety, we are interested only in the variables that we both read and write to and that pertain to a particular Web conversation. If the variable is for read-only use or it is application-wide, then no harm results in sharing this memory space across all instances. For all other variable uses, we want to make sure that we either have synchronized access to the variable (more on this in a moment) or that we have a unique variable for each thread.
To ensure we have our own unique variable instance for each thread, we simply move the declaration of the variable from within the class to within the method using it. We have now changed our variable from an instance variable to a local variable. The difference is that, for each call to the method, a new variable is created; therefore, each thread has its own variable. Before, when the variable was an instance variable, the variable was shared for all threads processing that class instance. The following thread-safe code has a subtle, yet important, difference. Notice where the counter variable is declared!
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import java.math.*;
public class SimpleServlet extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
doPost(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//A variable that IS thread-safe!
private int counter = 0;
resp.getWriter().println("<HTML><BODY>");
resp.getWriter().println(this + ": <br>");
for (int c = 0; c < 10; c++)
{
resp.getWriter().println("Counter = " + counter + "<BR>");
try
{
Thread.currentThread().sleep((long) Math.random() * 1000);
counter++;
}
catch (InterruptedException exc) { }
}
resp.getWriter().println("</BODY></HTML>");
}
}
Move the variable declaration to within the doGet() method and test again. Notice a change in behavior? I know, you're thinking; "It can't be that easy," but usually it is.
As you scramble to revisit your latest servlet code to check where you declared your variables, you may run into a small snag.
As you move your variables from within the class definition to within the method, you may find that you were leveraging the
scope of the variable and accessing it from within other methods. If you find yourself in this situation, you have a couple
of choices. First, change the method interfaces and pass this variable (and any other shared variables) to each method requiring
it. I highly recommend this approach. Explicitly passing your data elements from method to method is always best; it clarifies
your intentions, documents each method's requirements, makes your code well structured, and offers many other benefits.
| Subject |
|
|
|
|
|
( 1 2 all )
|
|
|
|
|
Nice!By Anonymous on June 26, 2009, 3:44 pmA clear, concise technical article is rare. This is one of them. Well written and very accessible. Makes threading issues with Servlets much less scary.
Reply | Read entire comment
Great narration of the conceptBy Anonymous on June 25, 2009, 5:47 pmExcellent article. I learnt something new today
Reply | Read entire comment
Excellent articleBy Anonymous on June 25, 2009, 5:43 pmExcellent article
Reply | Read entire comment
Thank youBy Anonymous on June 17, 2009, 5:54 amReally nice straight to the point post.
Reply | Read entire comment
Good oneBy kveeramani on June 15, 2009, 12:01 pmIts well written with example.
Reply | Read entire comment
View all comments