Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 346|回复: 0

[servlet学习]Servlet线程同步实例研究

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-10 04:58:19 | 显示全部楼层 |阅读模式
    今天的中间件课程上,Dash老师给出了一段代码,用于统计一个Servlet被访问的次数,代码如下:

      
          
           public class Counter extends HttpServlet {
            int count = 0; // 记录servlet被访问的次数
            public void doGet(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                    response.setContentType("text/HTML");
                    PrintWriter out = response.getWriter();
                    count++;   // 访问次数加一
                    out.println("The page has been accessed:"+count+"<BR>");
                   
            }
            public void doPost(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                    this.doGet(request, response);
            }
    }
                                  
          
           咋看上去,没有任何问题,好的,我们来实际运行一下,连续访问如下url五次:                连续访问五次,页面显示访问次数正确。但这并不代表程序没有问题,请考虑以下情况,客户端A和客户端B同时对Counter Servlet进行访问,那么必然需要同时对counter进行自增操作,竞争条件出现了!大家想一想,这和操作系统课程中的经典的竞争条件是不是一模一样。        要理解上面的话,首先要先理解Servlet的线程模型:           图一:Servlet线程模型,来自java Eye        我们知道,当Counter类被初始化之后,Servlet容器中便保存了一个该对象的实例(注意有且仅有一个实例,还有一个条件,Counter类没有实现SingleThreadModel接口),如果存在两个客户端同时访问该Servlet,那么Servlet容器会从该Servlet对象的线程池中分配两个线程分别处理两个客户端的请求。在Dash老师给出的代码中,如果多个客户端同时访问这个Servlet,相应的必然存在多个线程同时访问Counter类的实例变量count,必然会导致数据竞争问题,为了更明显的演示这一问题,我先来给Dash老师的代码加点料,修改后的代码如下:           
         
          public class Counterbeta1 extends HttpServlet {
            int count; //记录页面被访问的次数
            public void doGet(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                    response.setContentType("text/html");
                    PrintWriter out = response.getWriter();
                    count++;   // 访问次数加1
                    try
                    {Thread. sleep (5000); //为了突出并发问题,在这设置一个延时
                    }
                    catch(Exception e)
                    {e.printStackTrace();} //如果你还记得的话,在操作系统的线程同步试验中,
                                           //我们多次用这种技巧放大问题
                    out.println("The page has been accessed:"+count+"<BR>");
            }
            public void doPost(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
               this.doGet(request, response);
            }
    }
                    做如下的实验,打开两个浏览器窗口,输入相同的url访问Counterbeta1,首先在第一个窗口中敲回车确认访问,再迅速的在第二个浏览器中窗口中敲回车,结果如下:         显然,结果出错,两个浏览器窗口中显示的访问次数都是2,显然正确的结果应该是一个为1,另外一个为2。数据竞争出现了,好了,让我再玩的损一点,给Dash老师的代码再加点料:         
       
        public class Counterbeta2 extends HttpServlet {
            int count;
           
            public void doGet(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                    response.setContentType("text/html");
                    PrintWriter out = response.getWriter();
                    int count_copy = count;
                    try
                    {
                            Thread. sleep (5000); //为了突出并发问题,在这设置一个延时
                    }
                    catch(Exception e)
                    {e.printStackTrace();} //如果你还记得的话,在操作系统的线程同步试验中,
                                           //我们多次用这种技巧放大问题
                    count_copy++;
                    count = count_copy;
                   
                    out.println("The page has been accessed:"+count+"<BR>");
            }
            public void doPost(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
            this.doGet(request, response);
            }
    }
                               
               再实验一下,开两个浏览器窗口,方法同上,结果如下:         
               这下子错的更离谱了,两个页面显示的访问次数都是1。
               下面给出线程安全版的计数器:
               public class Countersafe extends HttpServlet {
        int count;
            public void doGet(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                    response.setContentType("text/html");
                    PrintWriter out = response.getWriter();
                    synchronized(this)
                    {
                       count++;   // 访问次数加一
                       try
                            {Thread. sleep (5000); //为了突出并发问题,在这设置一个延时
                            }
                            catch(Exception e)
                            {e.printStackTrace();} //如果你还记得的话,在操作系统的线程同步试验中,
                                                   //我们多次用这种技巧放大问题
                       out.println("The page has been accessed:"+count+"<BR>");
                    }
            }
           
            public void doPost(HttpServletRequest request, HttpServletResponse response)
                            throws ServletException, IOException {
                this.doGet(request, response);
            }
    }  
                
             
                
              注意,上面给出的线程安全版依旧是为了放大问题而加入了线程睡眠的语句,请注意判断。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-23 00:31 , Processed in 0.418885 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表