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. scanf()中清除输入缓冲区的几种方法归纳

    应用场景:我们使用多个scanf()的时候,如果输入缓冲区还有数据的话,那么scanf()就不会询问用户输入,而是直接就将输入缓冲区的内容拿出来用了,这就导致了前面的错误影响到后面的内容,为了隔离这种 ...

  2. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第一章:创建基本的MVC Web站点

    在这一章中,我们将学习如何使用基架快速搭建和运行一个简单的Microsoft ASP.NET MVC Web站点.在我们马上投入学习和编码之前,我们首先了解一些有关ASP.NET MVC和Entity ...

  3. angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用

    今天我们要讲的是ng2的路由系统. 例子

  4. Effective前端2:优化html标签

    div { float: left; } .keyboard > div + div { margin-left: 8px; } --> div{display:table-cell;ve ...

  5. Visual Studio:error MSB8020(搬运)

    状况如下: error MSB8020: The builds tools for v120 (Platform Toolset = 'v120') cannot be found. To build ...

  6. 设计模式C#合集--抽象工厂模式

    抽象工厂,名字就告诉你是抽象的了.上代码. public interface BMW { public void Drive(); } public class BMW730 : BMW { publ ...

  7. 无法访问org.springframework.core.NestedRuntimeException 找不到org.springframework.core.NestedRuntimeException的类文件

    在学习springAOP时,出现如下异常: 无法访问org.springframework.core.NestedRuntimeException 找不到org.springframework.cor ...

  8. Struts2入门(五)——OGNL和标签库

    一.前言 OGNL和标签库的作用,粗暴一点说,就是减少在JSP页面中出现java代码,利于维护. 1.1.OGNL 1.1.1.什么是OGNL? OGNL(Object-Graph Navigatio ...

  9. Supermap iCloudManager -负载均衡

    Supermap icm负载均衡理解: 应用场景:地图出图 子节点1和子节点2中的服务保持一致,一般情况下设置的是匿名用户通过nginx访问服务信息,所以不需要登录. 1.通过nginx分发请求,(轮 ...

  10. Hilbert-Huang Transform(希尔伯特-黄变换)

    在我们正式开始讲解Hilbert-Huang Transform之前,不妨先来了解一下这一伟大算法的两位发明人和这一算法的应用领域 Section I 人物简介 希尔伯特:公认的数学界“无冕之王”,1 ...