Servlet 有关线程安全的问题及解决方式

Servlet的线程安全

  • 由于 Servlet 采用的是单例模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全

  • 模拟用户登录功能来查看 Servlet 线程是否安全

  • 结论:一个浏览器代表一个线程,多个浏览器代表多个线程。按理说我们期望的应该是每个浏览器查看的都应该是自己的用户名。而现在的结果是浏览器中数据混乱。因此,我们可以认为 Servlet 是线程不安全的!

  • 解决:定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其他时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全问题了,可以将其定义到 doGet 或 doPost 方法内或者使用同步功能即可。

  • 案例演示

    1. 新建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);
          }
      }
      
    2. 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>
      
    3. 演示

      • 因为需要演示线程安全,所以需要两个浏览器模拟两个线程,所以要开两个浏览器

      • 谷歌浏览器中url传递参数username=aaa

      • 火狐浏览器中url传递参数username=bbb

      • 然后谷歌浏览器先访问,紧接着火狐浏览器访问

      • 结果现象是,俩浏览器都是welcome:bbb

    4. 解决方式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);
          }
      }
      
      
    5. 解决方式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);
          }
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值