从零开始学习JavaWeb - 初窥Servlet线程安全问题

1. 线程安全的概念

  • 服务器在收到请求之后,会启动一个线程来进行相应的请求处理。

  • 默认情况下,服务器为每个Servlet只创建一个对象实例。当多个请求访问同一个Servlet时,会有

    多个线程访问同一个Servlet对象,此时就可能发生线程安全问题。

  • 多线程并发逻辑,需要使用synchronized对代码加锁处理,但尽量避免使用。

2. 线程安全的案例

  • ThreadServlet

    package cn.knightzz.servlet;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * @author 王天赐
     * @title: ThreadServlet
     * @projectName spring-aop-01
     * @description:
     * @website http://knightzz.cn/
     * @github https://github.com/knightzz1998
     * @date 2022/1/9 9:56
     */
    @WebServlet(name="ThreadServlet", urlPatterns = "/thread-servlet")
    public class ThreadServlet extends HttpServlet {
    
        /**
         * 共享变量
         */
        private String name;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 获取参数
            name = req.getParameter("name");
    
            // 睡眠5s, 模拟并发
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            // 获取输出流
            PrintWriter writer = resp.getWriter();
            writer.println("<h2>" + name + "</h2>");
            writer.close();
        }
    }
    
    
  • thread.html

    使用三个 iframe 是为了模拟并发访问

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Servlet线程安全测试</title>
    </head>
    <body>
    
    <iframe width="400px" height="100px" src="thread-servlet?name=zhang"></iframe>
        <br/>
    <iframe width="400px" height="100px" src="thread-servlet?name=wang"></iframe>
        <br/>
    <iframe width="400px" height="100px" src="thread-servlet?name=li"></iframe>
    
    </body>
    </html>
    
  • 下图为执行结果, 如下图所示 , 正常情况下, 每个iframe 的值是不同的, 但是由于并发访问, 此时显示都是 <h2>li</h2>

image-20220109102640748

3. 线程安全的解决方案

  • 在共享变量上面加锁

     @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            synchronized (this) {
                // 获取参数
                name = req.getParameter("name");
    
                // 睡眠5s, 模拟并发
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                // 获取输出流
                PrintWriter writer = resp.getWriter();
                writer.write("<h2>" + name + "</h2>");
                writer.close();
            }
        }
    
  • 执行结果如下

    image-20220109103554340

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兀坐晴窗独饮茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值