Servlet/jsp技術和asp、php等相比,由于其多線程運行而具有很高的執行效率。
由于Servlet/JSP默認是以多線程模式執行的,所以,在編寫代碼時需要非常細致地考慮多線程的同步問題。
如果在編寫Servlet/JSP程序時不注意到多線程的同步問題,這往往造成程序在少量用戶訪問時沒有任何問題,而在并發用戶上升到一定值時,就會經常出現一些莫名其妙的問題,對于這類隨機性的問題調試難度也很大。
比如下面這個程序就有問題。
存在多線程問題的程序例子
這個例子中,首先有一個JSP頁面,其中有一個簡單的表單:
提交表單后,轉向一個Servlet進行處理:
獲取請求中的參數,并且調用setAttribute方法將其值存儲,轉向下一個jsp頁面:
復制代碼 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頁面中將該值顯示出來:
username: <%= request.getAttribute(“username”)%>
這樣做有什么問題呢?
打開瀏覽器,輸入訪問地址后,輸入一個用戶名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()方法。
(這種方式了解一下就行了)。
新聞熱點
疑難解答