避免servlet 在併發下線程安全問題

servlet是何物?

1,http://www.ibm.com/developerworks/cn/java/j-lo-servlet/

2,http://baike.baidu.com/link?url=2-VdfHr9F_FmPpe3Q3T_YNWRkUOAosodZpeTd-4qZHZwsHqg-w-hJ3fGM13Hkpe7Rrq1MTzjZrMznkgv80G_X_

    由於 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

blob.png

2,在瀏覽器 B(Safari)輸入:second

blob.png

執行的次序:先點擊 chrome ,並且在10秒之內點擊了 Safari ,等待執行結果。模擬併發訪問。有興趣的讀者可以使用第三個瀏覽器訪問,並且在10秒內分別提交;不出意外最後執行的結果應該是顯示最後提交的那個數據。


頁面執行結果:

blob.png

後臺打印的結果如下:

blob.png

簡單分析:

 * 從結果可以判斷出每個請求各自啓動了一個線程處理請求;

 * 當用戶在瀏覽器 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章