Servlet/JSP技術和ASP、PHP等相比,因爲其多線程運行而具備很高的執行效率。java
因爲Servlet/JSP默認是以多線程模式執行的,因此,在編寫代碼時須要很是細緻地考慮多線程的同步問題。後端
若是在編寫Servlet/JSP程序時不注意到多線程的同步問題,這每每形成程序在少許用戶訪問時沒有任何問題,而在併發用戶上升到必定值時,就會常常出現一些莫名其妙的問題,對於這類隨機性的問題調試難度也很大。瀏覽器
好比下面這個程序就有問題。多線程
這個例子中,首先有一個JSP頁面,其中有一個簡單的表單:併發
<form action="MultiThreadServlet"> <input type="text" name="username"> <input type="submit" value="submit"> </form>
提交表單後,轉向一個Servlet進行處理:jsp
獲取請求中的參數,而且調用setAttribute方法將其值存儲,轉向下一個jsp頁面:spa
package com.shengqishiwind.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MultiThreadServlet extends HttpServlet { //使用成員變量 private String username; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //從請求中獲得參數,即用戶名 username = request.getParameter("username"); //獲得當前線程的名字 System.out.println("Thread Name: " + Thread.currentThread().getName()); //模擬一些後端的業務處理 try { Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } request.setAttribute("username", username); //請求轉發 request.getRequestDispatcher("hello.jsp").forward(request, response); } }
中間讓線程停留了10秒鐘,來模擬一些操做。線程
在下一個JSP頁面中將該值顯示出來: 調試
<body> username: <%= request.getAttribute("username")%> </body>
這樣作有什麼問題呢?code
打開瀏覽器,輸入訪問地址後,輸入一個用戶名zhangsan,再打開一個窗口,輸入用戶名lisi。
兩個瀏覽器窗口都提交之後,過了必定時間,能夠看到兩邊返回值都是lisi。
Servlet的多線程同步問題:
Servlet自己是單實例的,這樣當有多個用戶同時訪問某個Servlet時,會訪問該惟一的Servlet實例中的成員變量,若是對成員變量進行寫入操做,那就會致使Servlet的多線程問題,即數據不一致。
1.解決Servlet多線程同步問題的最好方式:
去除實例變量,使用局部變量。
好比上面那個例子修改以下:
public class MultiThreadServlet extends HttpServlet { //使用成員變量 //private String username; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //從請求中獲得參數,即用戶名 String username = request.getParameter("username"); //獲得當前線程的名字 System.out.println("Thread Name: " + Thread.currentThread().getName()); //模擬一些後端的業務處理 try { Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } request.setAttribute("username", username); //請求轉發 request.getRequestDispatcher("hello.jsp").forward(request, response); } }
不使用成員變量,而使用局部變量,由於局部變量在每一個線程中都有各自的實例。
因此對Servlet來講,若是要對某個變量作寫入操做,必定不要使用成員變量,而要使用局部變量。
2.使用同步代碼塊
synchronized{}
3.Servlet實現javax.serlvet.SingleThreadModel(Servlet2.4中已經廢棄了該接口),此時Servlet容器將保證Servlet實例以單線程方式運行,也就是說,同一時刻,只會有一個線程執行Servlet的service()方法。
(這種方式瞭解一下就好了)。
聖思園張龍老師Java Web視頻教程。