servlet是何物?
1,http://www.ibm.com/developerworks/cn/java/j-lo-servlet/
由於 servlet 是單例的,所以所有對這個 servlet 的請求都是共享一個實例 因此導致成員變量 username 對於所有請求這個 servlet 的請求都是共享了,在多線程情況下就回導致 username 的值衝突,結果請看演示;用兩個不同的瀏覽器(模擬兩個線程)分別請求這個 servlet,並且輸入不同的值,跳轉之後兩個show.jsp顯示的值是後面點擊提交的值;下面代碼演示這個問題;
package com.ubuntuvim.servlet.concurrent;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author ubuntuvim
* @Email [email protected]
* @2015年8月21日 上午1:54:09
*/
public class ConcurrentServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 成員變量,由於 servlet 是單例,所以每個 request 請求後的線程公用一個 servlet 實例
private String username;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 從頁面獲取
//String username = request.getParameter("username");
this.username = request.getParameter("username");
System.out.println("thread name = " + Thread.currentThread().getName());
System.out.println("username = " + this.username);
// 模擬業務處理
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
request.setAttribute("username", username);
System.out.println("username >>> " + this.username);
request.getRequestDispatcher("show.jsp").forward(request, response);
}
}
執行
1, 在瀏覽器 A(chrome)輸入 :first
2,在瀏覽器 B(Safari)輸入:second
執行的次序:先點擊 chrome ,並且在10秒之內點擊了 Safari ,等待執行結果。模擬併發訪問。有興趣的讀者可以使用第三個瀏覽器訪問,並且在10秒內分別提交;不出意外最後執行的結果應該是顯示最後提交的那個數據。
頁面執行結果:
後臺打印的結果如下:
簡單分析:
* 從結果可以判斷出每個請求各自啓動了一個線程處理請求;
* 當用戶在瀏覽器 A(線程A) 提交 username 時候,執行到(
1 |
this .username = request.getParameter( "username" ); |
),把得到的值設置到 username 上,
* 此時休眠10秒,休眠的時候瀏覽器 B(線程B) 也提交了,在 A請求還沒執到(
request.setAttribute("username", username);
)期間 B 也執行了(
1 |
request.getParameter( "username" ) |
),
* 此時 username 的值就是 B 提交的值了;
* 然後 A 休眠完成,此時 username 的值已經不是 A 請求的值了!!
*
* 解決辦法:
* 不把username 定義爲成員變量,而是定義爲方法內部的局部變量,每個線程的都各自有自己的方法區,自然方法內的變量是不可共享了!
* 此時 username 分別處在不同的線程,互不干擾,自然也就不會相互影響了!!
這是個很簡單例子,但是很能說明問題了。在以後使用 servlet 的時候就要注意了!!!
由此延伸出另一個問題:struts2的 action 會不會也有這個問題呢????
答案是不會,因爲 action 類是多例的,每個 request 請求都會實例化一個 action,每個實例是在各自的線程中的,自然也就不會出現線程的安全問題了……有興趣的你可以驗證看看或者看看官方文檔即可!
原文地址:http://ibeginner.sinaapp.com/index.php?m=Home&c=Index&a=detail&id=d32fe7029e8841f475b9573611230881