Detailed explanation of servlet thread safety issues

Abstract : This paper introduces the multi-threading mechanism of servlet, explains the reasons for the insecurity of servlet thread through an example and combines the memory model of Java, gives three solutions to ensure the safety of servlet thread, and explains the three solutions in actual development. trade-offs.

 

   Compared with ASP and PHP, Servlet/JSP technology has high execution efficiency due to its multi-threaded operation. Since Servlet/JSP is executed in multi-threaded mode by default, the security of multi-threading needs to be carefully considered when writing code. However, many people do not pay attention to the problem of multi-thread safety when writing Servlet/JSP programs, which often causes the program to be written without any problems when accessed by a small number of users, but will often appear when the number of concurrent users rises to a certain value. Some baffling questions.

 

  Servlet's multi-threading mechanism The   Servlet architecture is based on the Java multi-threading mechanism, and its life cycle is in charge of the Web container. When the client requests a servlet for the first time, the servlet container will instantiate the servlet class according to the web.xml configuration file. When a new client requests the servlet, the servlet class is generally not instantiated again, that is, multiple threads are using this instance. The servlet container will automatically use technologies such as thread pools to support the operation of the system, as shown in Figure 1.
  

                                        

  In this way, when two or more threads access the same servlet at the same time, it may happen that multiple threads access the same resource at the same time, and the data may become inconsistent. Therefore, if you do not pay attention to the thread safety problem when building a Web application with Servlet, the Servlet program written will have hard-to-find errors.

  Servlet thread safety problem

  Servlet thread safety problem is mainly caused by improper use of instance variables, here is a realistic example to illustrate.

copy code
 1 public class ConcurrentTest extends HttpServlet {
 2     PrintWriter output;
 3     @Override
 4     protected void service(HttpServletRequest request, HttpServletResponse response)
 5             throws ServletException, IOException {
 6         String  username;
 7         response.setContentType("text/html;charset=gb2312");
 8         username=request.getParameter("username");
 9         output=response.getWriter();
10         try {
11             //In order to highlight the concurrency problem, set a delay here 
12              Thread.sleep(5000 );
 13              output.println("username:"+username+"<BR>" ); 
 14          } catch (Exception e) {
 15              e.printStackTrace ();
 16          }
 17      }
 18 }
copy code

  An instance variable output is defined in the servlet, which is assigned as the user's output in the service method. When a user accesses the servlet, the program will run normally, but when multiple users access it concurrently, there may be a problem that other users' information is displayed on other users' browsers. This is a serious problem. In order to highlight the concurrency problem and facilitate testing and observation, we performed a delayed operation when echoing user information. Assuming that the servlet has been registered in the web.xml configuration file, two existing users a and b access the servlet at the same time (you can start two IE browsers, or access them on two machines at the same time), that is, at the same time in the browser Enter:

  a: http://localhost:8080/ServletTest/ConcurrentTest?Username=a
  b: http://localhost:8080/ServletTest/ConcurrentTest?Username=b

  If user b is a little slower than user a's Enter time, you will get the output shown in Figure 2:

  

                                        

                                                          Figure 2 Browser output for user a and user b

  As can be seen from Figure 2, the web server starts two threads to process requests from user a and user b respectively, but a blank screen is obtained on user a's browser, and user a's information is displayed on user b's on the browser. The servlet is thread unsafe. Let's start by analyzing the memory model of the instance, and observe the value of the instance variable output at different times to analyze the reasons that make the servlet thread unsafe.

  Java's memory model JMM (Java Memory Model) JMM is mainly to specify some relationships between threads and memory. According to the design of JMM, there is a main memory (Main Memory) in the system. All instance variables in Java are stored in the main memory and are shared for all threads. Each thread has its own working memory (Working Memory). The working memory consists of two parts: the cache and the stack. The cache stores a copy of the variables in the main memory. The cache may not always be synchronized with the main memory, that is, the variables in the cache The modification of the thread may not be written to the main memory immediately; the thread's local variables are saved in the stack, and the threads cannot directly access the variables in the stack with each other. According to JMM, we can abstract the memory model of the servlet instance discussed in the paper into the model shown in Figure 3.

                                     

  Next, according to the memory model shown in Figure 3, when the threads of user a and b (referred to as a thread, b thread) are executed concurrently, the changes of variables involved in the Servlet instance and the execution of the threads are analyzed, as shown in Figure 4 shown.

            

  As can be clearly seen from Figure 4, since the modification of the instance variable output by thread b overwrites the modification of the instance variable output by thread a, the information of user a is displayed on the browser of user b. If the modification of output by thread b has not been flushed to the main memory when thread a executes the output statement, the output result shown in Figure 2 will not appear, so this is only an accidental phenomenon, but it increases the potential of the program. of danger.

   Designing thread-safe servlets

  Through the above analysis, we know that the incorrect use of instance variables is the main cause of servlet thread insecurity. Three solutions to this problem are given below and some reference suggestions are given for the selection of the solution.

  1. Implement the SingleThreadModel interface

  This interface specifies how the system handles calls to the same servlet. If a servlet is specified by this interface, then the service method in the servlet will not have two threads executed at the same time, and of course there is no thread safety problem. This method only needs to change the class header definition of the previous Concurrent Test class to:

1 public class ConcurrentTest extends HttpServlet implements SingleThreadModel  {
2       ...  ...      
3 }

  javax.servlet.SingleThreadModel API and its translation

  Ensures that servlets handle only one request at a time. This interface has no methods.

  Make sure the servlet only handles one request at a time. Interfaces do not contain methods.

  If a servlet implements this interface, you areguaranteedthat no two threads will execute concurrently in the servlet'smethod. The servlet container can make this guarantee by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet.   service 

  If the servlet implements this interface, it will ensure that no two threads will execute the servlet's service method at the same time. The servlet container does this by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances that will assign new requests to an idle servlet.

  Note that SingleThreadModel does not solve all thread safety issues. For example, session attributes and static variables can still be accessed by multiple requests on multiple threads at the same time, even when SingleThreadModel servlets are used. It is recommended that a developer take other means to resolve those issues instead of implementing this interface, such as avoiding the usage of an instance variable or synchronizing the block of the code accessing those resources. This interface is deprecated in Servlet API version 2.4.

  注意:SingleThreadModel不会解决所有的线程安全隐患。 例如,会话属性和静态变量仍然可以被多线程的多请求同时访问,即便使用了SingleThreadModel servlet。建议开发人员应当采取其他手段来解决这些问题,而不是实现该接口,比如 避免实例变量的使用或者在访问资源时同步代码块。该接口在Servlet API 2.4中将不推荐使用。

   2、同步对共享数据的操作

  使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。同步后的代码如下:

copy code
 1 public class ConcurrentTest extends HttpServlet {
 2     PrintWriter output;
 3     @Override
 4     protected void service(HttpServletRequest request, HttpServletResponse response)
 5             throws ServletException, IOException {
 6         String  username;
 7         response.setContentType("text/html;charset=gb2312");
 8         username=request.getParameter("username");
 9         synchronized(this){
10             output=response.getWriter();
11             try {
12                 //为了突出并发问题,在这设置一个延时
13                 Thread.sleep(5000);
14                 output.println("用户名:"+username+"<BR>"); 
15             } catch (Exception e) {
16                 e.printStackTrace();
17             }
18         }
19     }
20 }
copy code

  

  3、避免使用实例变量

  本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。

  修正上面的Servlet代码,将实例变量改为局部变量实现同样的功能,代码如下:

copy code
 1 public class ConcurrentTest extends HttpServlet {
 2     @Override
 3     protected void service(HttpServletRequest request, HttpServletResponse response)
 4             throws ServletException, IOException {
 5         PrintWriter output;
 6         String username;
 7         response.setContentType("text/html;charset=gb2312");
 8         username=request.getParameter("username");
 9         synchronized(this){
10             output=response.getWriter();
11             try {
12                 //为了突出并发问题,在这设置一个延时
13                 Thread.sleep(5000);
14                 output.println("用户名:"+username+"<BR>"); 
15             } catch (Exception e) {
16                 e.printStackTrace();
17             }
18         }
19     }
20 }
copy code

  Testing the above three methods shows that thread-safe servlet programs can be designed using them. However, if a servlet implements the SingleThreadModel interface, the servlet engine will create a separate servlet instance for each new request, which will cause a lot of overhead. SingleThreadModel is no longer advocated in Servlet2.4; also, if synchronization is used in the program to protect the shared data to be used, the performance of the system will be greatly reduced. This is because the synchronized code block can only have one thread executing it at a time, which reduces the throughput of processing client requests at the same time, and many clients are blocked. In addition, in order to ensure the consistency of the main memory content and the data in the working memory of the thread, the cache should be refreshed frequently, which will also greatly affect the performance of the system. Therefore, the synchronization code in Servlet should also be avoided or minimized in actual development; avoiding the use of instance variables in Serlet is the best choice to ensure Servlet thread safety. It can also be known from the Java memory model that the temporary variables in the method are allocated space on the stack, and each thread has its own private stack space, so they will not affect thread safety.

  Summary

  The thread safety problem of Servlet will only appear when there are a large number of concurrent accesses, and it is difficult to find, so special attention should be paid when writing Servlet programs. Thread safety problems are mainly caused by instance variables, so instance variables should be avoided in servlets. If the application design cannot avoid the use of instance variables, then use synchronization to protect the instance variables that will be used, but to ensure the best performance of the system, the code paths with the least availability should be synchronized.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325985371&siteId=291194637