Servlet是web體系里面最重要的部分,下面羅列幾道常見的面試題,小伙伴們一定要好好記住哈,
1.Servlet是單例的嗎,如何證明?
Servlet一般都是單例的,并且是多執行緒的,如何證明Servlet是單例模式呢?很簡單,重寫Servlet的init方法,或者添加一個構造方法,然后,在web.xml中配置,如:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>web.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
然后是MyServlet
public class MyServlet extends HttpServlet{
public MyServlet(){
System.out.println("MyServlet建構式呼叫了");
}
@Override
public void init() throws ServletException {
System.out.println("MyServlet初始化");
}
}
啟動Tomcat,不管你訪問多少次這個Servlet,init方法和構造器都只會執行1次,
2.如何讓Servlet變成多例
方法1.實作 SingleThreadModel 介面(不推薦,官方已經將這個介面廢棄)
public class MyServlet extends HttpServlet implements SingleThreadModel{
public MyServlet(){
System.out.println("MyServlet建構式呼叫了");
}
@Override
public void init() throws ServletException {
System.out.println("MyServlet初始化");
}
}
SingleThreadModel的意思是“單執行緒模式”,如果servlet實作了該介面,會確保不會有兩個執行緒同時執行servlet的service方法,
servlet容器通過同步化訪問servlet的單實體來保證,也可以通過維持servlet的實體池,對于新的請求會分配給一個空閑的servlet,原始碼中,最多會生成20個實體,
方法2. 在web.xml中多配置一個Servlet
哪怕是同一個Servlet,你在web.xml中配置幾個,就會有幾個實體,
3.你能證明Servlet執行緒不安全嗎?
Servlet默認是執行緒不安全的!
Servlet體系結構是建立在Java多執行緒機制之上的,它的生命周期是由Web容器負責的,
當客戶端第一次請求某個Servlet時,Servlet容器將會根據web.xml組態檔實體化這個Servlet類,
當有新的客戶端請求該Servlet時,一般不會再實體化該Servlet類,也就是有多個執行緒在使用這個實體,
Servlet容器會自動使用執行緒池等技術來支持系統的運行,
當兩個或多個執行緒同時訪問同一個Servlet時,可能會發生多個執行緒同時訪問同一資源的情況,資料可能會變得不一致,
所以在用Servlet構建的Web應用時如果不注意執行緒安全的問題,會使所寫的Servlet程式有難以發現的錯誤,
下面舉一個例子來說明,為什么Servlet是執行緒不安全的,
public class MyServlet extends HttpServlet{
String message;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
message = req.getParameter("message");
PrintWriter out = resp.getWriter();
//故意延時5秒鐘,使得下一次請求過來的時候,message的值還沒有回傳就被覆寫了
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
out.write(message);
out.flush();
out.close();
}
}
打開兩個瀏覽器,分別訪問:
http://localhost:8080/web/hello?message=jack
http://localhost:8080/web/hello?message=rose
因為有5秒的延時,所以可能就會出現第一個Servlet還沒回傳呢,第二個Servlet就進來了,于是,把message的值給沖掉了,如下圖
石錘了,Servlet是執行緒不安全的,
4.你怎么設計一個執行緒安全的Servlet?
1.最直接的辦法,就是用上面的SingleThreadModel介面
既然單例會有共享實體變數導致執行緒不安全的問題,那就改成多例的唄,
但是,這個介面都已經被官方廢棄了,這就說明官方也不推薦這么做,原因很簡單,那就是這樣一來會有很多個實體,性能的代價太大了,
-
用同步鎖
這也是非常容易想到的辦法,把當前物件鎖起來,不回傳不給其他用戶插入(怎么有點怪怪的?)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
synchronized(this){
message = req.getParameter("message");
PrintWriter out = resp.getWriter();
//故意延時5秒鐘,使得下一次請求過來的時候,message的值還沒有釋放
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
out.write(message);
out.flush();
out.close();
}
}
這樣的代價就是等待時間更長了,參考火車上的的衛生間,這就是同步鎖,
-
盡量別用實體變數,用區域變數代替
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553409.html
標籤:其他
上一篇:Rocksdb原理簡介
下一篇:返回列表
