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 )
|
|
|
|
|
Very Good materialBy Anonymous on November 2, 2009, 9:38 amVery good info is provided
Reply | Read entire comment
Thanks for this information.By Anonymous on November 2, 2009, 7:22 amThanks for this information.
Reply | Read entire comment
very helpful articleBy Anonymous on October 28, 2009, 6:43 amvery helpful article
Reply | Read entire comment
Well doneBy Anonymous on October 27, 2009, 12:37 pmthis article helped me a lot
Reply | Read entire comment
Still a problemBy Anonymous on October 14, 2009, 11:47 amShouldn't the counter variable being printed be 0-9 in each case, rather than 0-9 and 10-19? If you use this thread safe approach, the counter is not being re-set...
Reply | Read entire comment
View all comments