Servlet的线程安全
-
由于 Servlet 采用的是单例模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全
-
模拟用户登录功能来查看 Servlet 线程是否安全
-
结论:一个浏览器代表一个线程,多个浏览器代表多个线程。按理说我们期望的应该是每个浏览器查看的都应该是自己的用户名。而现在的结果是浏览器中数据混乱。因此,我们可以认为 Servlet 是线程不安全的!
-
解决:定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其他时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全问题了,可以将其定义到 doGet 或 doPost 方法内或者使用同步功能即可。
-
案例演示
-
新建ServletDemo04
package com.lichee.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ServletDemo04 extends HttpServlet{ //1.定义用户名成员变量 private String username = null; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //2.获取用户名 username = req.getParameter("username"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //3.获取输出流对象 PrintWriter pw = resp.getWriter(); //4.响应给客户端浏览器 pw.print("welcome:" + username); //5.关流 pw.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
Servlet配置
<servlet> <servlet-name>servletDemo04</servlet-name> <servlet-class>com.lichee.servlet.ServletDemo04</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletDemo04</servlet-name> <url-pattern>/servletDemo04</url-pattern> </servlet-mapping>
-
演示
-
因为需要演示线程安全,所以需要两个浏览器模拟两个线程,所以要开两个浏览器
-
谷歌浏览器中url传递参数username=aaa
-
火狐浏览器中url传递参数username=bbb
-
然后谷歌浏览器先访问,紧接着火狐浏览器访问
-
结果现象是,俩浏览器都是welcome:bbb
-
-
解决方式1:将username由成员变量,放到方法中
package com.lichee.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ServletDemo04 extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.定义用户名变量 String username = null; //2.获取用户名 username = req.getParameter("username"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //3.获取输出流对象 PrintWriter pw = resp.getWriter(); //4.响应给客户端浏览器 pw.print("welcome:" + username); //5.关流 pw.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
解决方式2:使用同步代码块
package com.lichee.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ServletDemo04 extends HttpServlet{ //1.定义用户名成员变量 private String username = null; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { synchronized (this) { //锁需要唯一,Servlet对象就是唯一的,所以用this //2.获取用户名 username = req.getParameter("username"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //3.获取输出流对象 PrintWriter pw = resp.getWriter(); //4.响应给客户端浏览器 pw.print("welcome:" + username); //5.关流 pw.close(); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-