If you write Web applications in Java, the servlet is your best friend. Whether you write Java ServerPages (JSP) or plain servlets, you are both at the servlet's mercy and the lucky recipient of what the servlet has to offer. So let's look closer at the servlet.

As we know, when building a Website, we are primarily interested in the servlet's two methods: doPost() and doGet(). The underlining method for both methods is service(). Later, I look closer at these methods and how they relate to thread safety, but first let's review the concept of a thread.

A thread is a single execution process; in other words, an individual, sequential flow of control within a program. When we say that a program is multithreaded, we are not implying that the program runs two separate instances simultaneously (as if you concurrently executed the program twice from the command line). Rather, we are saying that the same instance (executed only once) spawns multiple threads that process this single instance of code. This means that more than one sequential flow of control runs through the same memory block.

So what do we mean by thread-safe, you ask? When multiple threads execute a single instance of a program and therefore share memory, multiple threads could possibly be attempting to read and write to the same place in memory. Let's look at an example. If we have a multithreaded program, we will have multiple threads processing the same instance (see Figure 1).

What happens when Thread-A examines variableinstanceVar? Notice how Thread-B has just incrementedinstanceVar. 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. This issue is not unique to servlets. It is a common programming problem only present when multithreading an application. You are probably thinking; "Well I didn't ask for multithreading. I just want a servlet!" And a servlet is what you have. Let me introduce you to our friend the servlet container.

Your servlet container is no dummy

A lot of magic happens between the Web browser's HTTP request and the code we write within the doGet() and doPost()methods. The servlet container makes this "magic" possible. Like any Java program, the servlet must run within a JVM, but for a Web application, we also have the complexity of handling HTTP requests—that's where the servlet container comes in. The servlet container is responsible for your servlets' creation, destruction, and execution; the sequence of these events is referred to as the servlet's lifecycle.

The servlet's lifecycle is an important topic, and thus, you will find it on Sun's Java certification exam. The reason for its importance is primarily because so much of the servlet's lifecycle is outside the programmer's control. We do not worry a lot (for the most part) about how many of our servlet's instances exist at runtime. Nor are we generally concerned about memory utilization regarding the creation and destruction of our servlets. The reason for our lack of concern is because the servlet container handles this for us (yes, more magic).

The servlet container not only handles the servlet's lifecycle, it also does a fine job at it. The servlet container is concerned about efficiency. It ensures that when servlets are created, they are utilized efficiently, and, yes, you guessed it, this includes multithreading. As Figure 2 illustrates, multiple threads simultaneously process your servlet.

Just imagine the performance problems we would experience with a Website as popular as Google or Amazon if the sites were not built using efficient, multithreaded processing. Though our Web application is probably not quite as popular as Google, it still would not be practical to build a site that required a servlet to be instantiated for each request. For that reason, I'm thankful that the servlet container handles the multithreading for me. Otherwise, most of us would have to change the way we designed our servlets.

Now that we are familiar with the perils of multithreaded applications and we know that all of our servlets are multithreaded, let's look at exactly when multithreading will be a problem for us.

Are you thread-safe?

Below is a simple servlet that is not thread-safe. Look closely, because at first glance, nothing appears wrong with it:

package threadSafety;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import java.math.*;
public class SimpleServlet extends HttpServlet
{
//A variable that is NOT thread-safe!
private int counter = 0;
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
doPost(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
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>");
}
}

The variable counter is an instance variable, called such because it is tied to the class instance. Because it is defined within the class definition, it belongs within that class instance. It's convenient to place our variables within this scope because it lives outside each of the class's methods and can be accessed at any time. The value is also retained between method calls. The problem here is that our servlet container is multithreaded and shares single instances of servlets for multiple requests. Does defining your variables as instance variables sound like a good idea now? Remember, only one place in memory is allocated for this variable, and it is shared between all threads that intend on executing this same class instance.

Let's find out what happens when we execute this servlet simultaneously. We add a delay in processing by using the sleep() method. This method helps simulate more accurate behavior, as most requests differ in the amount of time required for processing. Of course, as is our luck as programmers, this also causes our problem to occur more often. This simple servlet will increment counter such that each servlet should be able to display sequential values. We create simultaneous requests by using HTML frames; each frame's source is the same servlet:

<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.

Your first defense: Avoidance

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.

If you discover that you must share a variable between servlets and this variable is going to be read from and written to by multiple threads (and you are not storing it in a database), then you will require thread synchronization. Sorry, there is no way around it now.

Your second defense: Partial synchronization

Thread synchronization is an important technique to know, but not one you want to throw at a solution unless required. Anytime you synchronize blocks of code, you introduce bottlenecks into your system. When you synchronize a code block, you tell the JVM that only one thread may be within this synchronized block of code at a given moment. If we run a multithreaded application and a thread runs into a synchronized code block being executed by another thread, the second thread must wait until the first thread exits that block.

It is important to accurately identify which code block truly needs to be synchronized and to synchronize as little as possible. In our example, we assume that making our instance variable a local variable is not an option. Look at how we would synchronize the crucial block of code:

package threadSafety;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import java.math.*;
public class SimpleServlet extends HttpServlet
{
//A variable that is NOT thread-safe!
private int counter = 0;
private String mutex = "";
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
doPost(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
resp.getWriter().println("<HTML><BODY>");
resp.getWriter().println(this + ": <br>");
synchronized (mutex)
{
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>");
}
}

Notice how the synchronized code block begins where we first begin to access the variable and ends when we are done with it. You must either synchronize an entire method or use an object. In our example, I created a string that is an instance variable. This string is called a mutex (short for mutual exclusion) and is shared among all threads, thus ensuring that only one thread has access to the specified code block at any time.

In our simple example, the synchronization block is small. If it weren't small, we would want to carefully analyze the logic and possibly refactor the code such that anything that can be done asynchronously is not within the synchronized block. In the Web world, predicting your largest load is not always easy, and when introducing a synchronized code block, every millisecond counts.

In the event you find yourself with more synchronized code than nonsynchronized code and your estimated load is minimal, you have yet another choice. Simply put, just tell the servlet container your servlet is not thread-safe.

Your last defense: Whole synchronization

To tell the servlet container your thread is not thread-safe, implement an empty interface. Empty interfaces are used as tags, so other classes can check for the interface and act accordingly. The tag we want to implement is thejavax.servlet.SingleThreadModel interface. The servlet container specification states that if your servlet implements this interface, you will be guaranteed that no two threads will simultaneously execute your servlet's service() method. Remember,service() is the underlining implementation for both the doGet() and doPost()methods, so we're safe. The following code snippet illustrates this interface's usage:

package threadSafety;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import java.math.*;
public class SimpleServlet extends HttpServlet implements SingleThreadModel
{
//A variable that is NOT thread-safe!
private int counter = 0;
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
doPost(req, resp);
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
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>");
}
}

Naturally, the easiest approach is the most expensive. I would hate to dig into a project and see a servlet tagged by the SingleThreadModel interface unless the servlet was intended to be used in such a manner and the architecture supports this behavior. Use this method only after much consideration and try to avoid putting unnecessary constraints on your application.

Feeling thread-safe?

We now have an introduction to thread safety and the servlet container. We know a little about the servlet's lifecycle, how servlets and thread safety are related, as well as what our options are. Thread safety and multithreading are interesting topics and I encourage you to learn more about them.

The next time you experience inconsistent results and inaccurate data (especially results from references to objects) from your favorite servlet, start looking at your instance variables. Armed with this knowledge, you should be able to sniff out non-thread-safe code fairly quickly. Remember to look for variables that are both instance variables and used for referencing conversation-specific data. Go ahead, pull that thread.

Phillip Bridgham, M.S., is a technologist for Comtech Integrated Systems and a multitalented IT professional with more than 14 years of industry experience. He has worked in the e-commerce, industrial automation, business, research and development, financial, medical and embedded systems industries. Bridgham is a Sun Certified Web Component Developer and Sun Certified Java Programmer and is currently teaching Java courses at California State University, Sacramento, College of Continuing Education.

From:

http://www.javaworld.com/article/2072798/java-web-development/write-thread-safe-servlets.html

Write thread-safe servlets [reproduced]的更多相关文章

  1. 【转】php Thread Safe(线程安全)和None Thread Safe(NTS,非 线程安全)之分

    Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. ...

  2. PHP版本VC6与VC9、Thread Safe与None-Thread Safe等的区别

    PHP版本VC6与VC9.Thread Safe与None-Thread Safe等的区别 [摘要]PHP 是一种 HTML 内嵌式的语言,是一种在服务器端执行的嵌入HTML文档的脚本语言,在PHP发 ...

  3. Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分

    Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. ...

  4. PHP版本VC6和VC9、Non Thread Safe和Thread Safe的区别

    链接:http://www.cnblogs.com/neve/articles/1863853.html 想更新个PHP的版本,PHP的windows版本已经分离出来了,见http://windows ...

  5. PHP5.3中关于VC9和VC6以及Thread Safe和Non Thread Safe版本选择的问题

    转自:http://www.htmer.com/article/716.htm 最近在PHP官网上看到又有新版的PHP下载了,于是上去找找For Windows的版本,可是一看确傻眼了,一共给了四个版 ...

  6. PHP的(Thread Safe与Non Thread Safe)

    在安装xdebug到时候你会有有TS和NTS版本的选择,在以前还有VC6和VC9的版本.如果你没有根据你目前的服务器的状况选择对应的版本的话,那么xdebug是安装不成功的. 一.如何选择 php5. ...

  7. 转:PHP的(Thread Safe与Non Thread Safe)

    在安装xdebug到时候你会有有TS和NTS版本的选择,在以前还有VC6和VC9的版本.如果你没有根据你目前的服务器的状况选择对应的版本的话,那么xdebug是安装不成功的. 一.如何选择 php5. ...

  8. Windows下PHP(Thread Safe与Non Thread Safe)版本说明

    转载“http://www.taoz11.com/archives/300.html” linux下直接下载源码,在服务器上编译即可,发现windows下有4个版本: VC9 x86 Non Thre ...

  9. PHP版本VC6与VC9/VC11/VC14、Thread Safe与None-Thread Safe等的区别

    最近正好在弄一个PHP的程序,在这之前一直没有怎么以接触,发现对PHP版本知识了解不是很清楚,自己看了不少类似的文章,还是感觉不够明确和全面, 网上的结论又都是模棱两可,在此,给出最完整甚至武断的解释 ...

  10. windows zend_guard+apache no ssl+php no Thread Safe fastcgi模式 环境配置

    最近公司要做代码加密,就采用ZEND GUARD 方式加密代码 并进行显示 此文为总结,以备自己以后查看和给需要的同学们参考 采用的php为5.3版本  由于现在加密的更改, 能支持zend guar ...

随机推荐

  1. ABP文档 - Hangfire 集成

    文档目录 本节内容: 简介 集成 Hangfire 面板授权 简介 Hangfire是一个综合的后台作业管理器,可以在ABP里集成它替代默认的后台作业管理器,你可以为Hangfire使用相同的后台作业 ...

  2. Partition:Partiton Scheme是否指定Next Used?

    在SQL Server中,为Partition Scheme多次指定Next Used,不会出错,最后一次指定的FileGroup是Partition Scheme的Next Used,建议,在执行P ...

  3. 网站定位之---根据IP获得区域

    记得以前做一个培训机构网站时候需要定位,那时候用的搜狐的api,不是很精准. demo:https://github.com/dunitian/LoTCodeBase/tree/master/NetC ...

  4. 07.LoT.UI 前后台通用框架分解系列之——轻巧的文本编辑器

    LoT.UI汇总:http://www.cnblogs.com/dunitian/p/4822808.html#lotui 上次说的是强大的百度编辑器 http://www.cnblogs.com/d ...

  5. tomcat开发远程调试端口以及利用eclipse进行远程调试

    一.tomcat开发远程调试端口 方法1 WIN系统 在catalina.bat里:  SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compi ...

  6. 数塔问题(DP算法)自底向上计算最大值

    Input 输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数 ...

  7. Flex 布局教程:实例篇

    该教程整理自 阮一峰Flexible教程 今天介绍常见布局的Flex写法.你会看到,不管是什么布局,Flex往往都可以几行命令搞定. 我的主要参考资料是Landon Schropp的文章和Solved ...

  8. 免费SSL证书 之Let’s Encrypt申请与部署(Windows Nginx)

    我着着皇帝的新衣,但是你看不见    有一颗愿意等待的心,说明你对未来充满希望.有一颗充满希望的心,那么等待又算什么.人就是在等待与希望中度过,我们永远要对未来充满信心! 读在最前面: 1.本文案例为 ...

  9. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  10. (转载)JAVA动态编译--字节代码的操纵

    在一般的Java应用开发过程中,开发人员使用Java的方式比较简单.打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了.这种开发模式背后的过程是:开发人员编写的 ...