美文网首页
Servlet 并发加锁

Servlet 并发加锁

作者: 旦暮何枯 | 来源:发表于2019-03-20 15:05 被阅读0次

    servlet 处理实现机制

    • 客户端发送请求 -> servlet 容器(Tomcat) -> 调度器 -> 在容器的线程池中选择一个工作组线程 -> 执行 servlet A 的 service 方法

    • 客户端发送第二条请求 -> servlet 容器(Tomcat)-> 调度器 -> 在容器的线程池中选择第二个工作组线程 -> 并发执行 servlet A 的 service 方法

    当有线程执行完毕,返回线程池
    当线程池中的所有线程都在执行服务,新来的请求排队
    当超过了设置的最大排队数量的请求到来时,容器会拒绝处理新的请求

    线程模型

    线程模型.png

    总结特点

    • 单实例
      • 容器中的 servlet 只会初始化一次,所有针对该 servlet 的请求,都只有这一个实例实例对象
    • 多线程
      • 多个请求的处理是由多个线程完成的,可以同时进行处理
    • 线程不安全
      • 多线程共用一个单实例对象,因为没有默认加锁操作,所以会出现同时请求一个实例对象不同操作,造成数据不一致的情况。

    线程安全

    变量线程安全
    • 参数变量本地化
      • 尽量使用局部变量,多线不共享局部变量
    • 使用同步块 synchronized
      • 对代码进行加锁处理,尽量缩小代码范围,不要在 service 方法中增加关键字,否则对性能损耗比较大
    属性的线程安全
    • ServletContext 线程安全
      • 多线程同时读写需要做数据同步的处理
    • HttpSession 理论上线程安全
      • 只能在处理同一个用户的 Session 时,被访问;用户打开多个属于同一个进程的的浏览器窗口时,这些窗口的访问属于同一个 session 会出现多次请求。需要多个工作线程的处理,可能也会和出现多个线程的读写属性的问题
    • ServletRequest 线程安全
      • 每一个请求只有一个工作线程处理,只能在同一个线程中被访问;所以安全
    避免在 Servlet 中创建线程

    servlet 本身就是在多线程中进行处理的,在 servlet 中再创建线程;会造成运行情况复杂,更容易发生多线程安全问题。

    多个 Servlet 访问外部对象加锁

    使用 synchronized 同步块进行加锁操作

    1. 创建一个 maven web-app
    2. 创建一个 SynchronizedTest 的 servlet 类
    3. 在 servlet 类中的 doGet 方法中使用 synchronized 标识进行加锁
    4. 在部署描述符中注册

    SynchronizedServlet 类

    public class SynchronizedServlet extends javax.servlet.http.HttpServlet {
    
        String name; // 为引入线程不安全问题引入实例变量,不推荐使用实例变量
        @Override
        public void init() throws ServletException {
            super.init();
        }
    
        @Override
        public void destroy() {
            super.destroy();
        }
    
        protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
            //使用 synchronized 标识进行代码加锁
            synchronized (this) {
                name = request.getParameter("username");//从 url 中获取 username 的值
                PrintWriter printWriter = response.getWriter();  // 创建 PrintWriter 对象,在页面中打印
                try {
                    //为模拟大量并发 设置线程休眠时间。
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                printWriter.print("username: " + name);
            }
        }
    }
    

    『项目目录』:https://github.com/wengfe/JAVA/tree/master/SynchronizedTest/src/main

    相关文章

      网友评论

          本文标题:Servlet 并发加锁

          本文链接:https://www.haomeiwen.com/subject/zsozmqtx.html