Servlet线程同步实例研究

转载 2012年03月28日 10:02:36

原文:http://blog.csdn.net/liuyimu/article/details/5456399

     今天的中间件课程上,Dash老师给出了一段代码,用于统计一个Servlet被访问的次数,代码如下:

     

  1. public class Counter extends HttpServlet {  
  2.   
  3.     int count = 0// 记录servlet被访问的次数  
  4.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  5.             throws ServletException, IOException {  
  6.   
  7.         response.setContentType("text/html");  
  8.         PrintWriter out = response.getWriter();  
  9.         count++;   // 访问次数加一  
  10.         out.println("The page has been accessed:"+count+"<BR>");   
  11.           
  12.     }  
  13.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  14.             throws ServletException, IOException {  
  15.   
  16.         this.doGet(request, response);  
  17.     }  
  18.   
  19. }  

     咋看上去,没有任何问题,好的,我们来实际运行一下,连续访问如下url五次:

    

 

       连续访问五次,页面显示访问次数正确。但这并不代表程序没有问题,请考虑以下情况,客户端A和客户端B同时对Counter Servlet进行访问,那么必然需要同时对counter进行自增操作,竞争条件出现了!大家想一想,这和操作系统课程中的经典的竞争条件是不是一模一样。

       要理解上面的话,首先要先理解Servlet的线程模型:

       图一:Servlet线程模型,来自Java Eye

       我们知道,当Counter类被初始化之后,Servlet容器中便保存了一个该对象的实例(注意有且仅有一个实例,还有一个条件,Counter类没有实现SingleThreadModel接口),如果存在两个客户端同时访问该Servlet,那么Servlet容器会从该Servlet对象的线程池中分配两个线程分别处理两个客户端的请求。在Dash老师给出的代码中,如果多个客户端同时访问这个Servlet,相应的必然存在多个线程同时访问Counter类的实例变量count,必然会导致数据竞争问题,为了更明显的演示这一问题,我先来给Dash老师的代码加点料,修改后的代码如下:

 

        

  1. public class Counterbeta1 extends HttpServlet {  
  2.   
  3.     int count; //记录页面被访问的次数  
  4.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  5.             throws ServletException, IOException {  
  6.   
  7.         response.setContentType("text/html");  
  8.         PrintWriter out = response.getWriter();  
  9.         count++;   // 访问次数加1  
  10.         try  
  11.         {Thread. sleep (5000); //为了突出并发问题,在这设置一个延时  
  12.         }  
  13.         catch(Exception e)  
  14.         {e.printStackTrace();} //如果你还记得的话,在操作系统的线程同步试验中,  
  15.                                //我们多次用这种技巧放大问题  
  16.         out.println("The page has been accessed:"+count+"<BR>");   
  17.     }  
  18.   
  19.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  20.             throws ServletException, IOException {  
  21.   
  22.        this.doGet(request, response);  
  23.     }  
  24.   
  25. }  

 

          做如下的实验,打开两个浏览器窗口,输入相同的url访问Counterbeta1,首先在第一个窗口中敲回车确认访问,再迅速的在第二个浏览器中窗口中敲回车,结果如下:

       显然,结果出错,两个浏览器窗口中显示的访问次数都是2,显然正确的结果应该是一个为1,另外一个为2。数据竞争出现了,好了,让我再玩的损一点,给Dash老师的代码再加点料:

       

  1. public class Counterbeta2 extends HttpServlet {  
  2.   
  3.   
  4.     int count;  
  5.       
  6.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  7.             throws ServletException, IOException {  
  8.   
  9.         response.setContentType("text/html");  
  10.         PrintWriter out = response.getWriter();  
  11.         int count_copy = count;  
  12.         try  
  13.         {  
  14.             Thread. sleep (5000); //为了突出并发问题,在这设置一个延时  
  15.         }  
  16.         catch(Exception e)  
  17.         {e.printStackTrace();} //如果你还记得的话,在操作系统的线程同步试验中,  
  18.                                //我们多次用这种技巧放大问题  
  19.         count_copy++;  
  20.         count = count_copy;  
  21.           
  22.         out.println("The page has been accessed:"+count+"<BR>");   
  23.     }  
  24.   
  25.   
  26.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  27.             throws ServletException, IOException {  
  28.   
  29.         this.doGet(request, response);  
  30.     }  
  31.   
  32. }  

       再实验一下,开两个浏览器窗口,方法同上,结果如下:

 

      

       这下子错的更离谱了,两个页面显示的访问次数都是1。

       下面给出线程安全版的计数器:

      

  1. public class Countersafe extends HttpServlet {  
  2.   
  3.     int count;  
  4.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  5.             throws ServletException, IOException {  
  6.   
  7.         response.setContentType("text/html");  
  8.         PrintWriter out = response.getWriter();  
  9.         synchronized(this)  
  10.         {  
  11.            count++;   // 访问次数加一  
  12.            try  
  13.             {Thread. sleep (5000); //为了突出并发问题,在这设置一个延时  
  14.             }  
  15.             catch(Exception e)  
  16.             {e.printStackTrace();} //如果你还记得的话,在操作系统的线程同步试验中,  
  17.                                    //我们多次用这种技巧放大问题  
  18.            out.println("The page has been accessed:"+count+"<BR>");   
  19.         }  
  20.     }  
  21.   
  22.       
  23.     public void doPost(HttpServletRequest request, HttpServletResponse response)  
  24.             throws ServletException, IOException {  
  25.   
  26.         this.doGet(request, response);  
  27.     }  
  28.   
  29. }  

      

      

      

      注意,上面给出的线程安全版依旧是为了放大问题而加入了线程睡眠的语句,请注意判断。

      附录:完整源代码下载地址:http://download.csdn.net/source/2210349

卖票小系统(关于线程同步的问题)

假设有两个窗口合起来要卖100张票,这就涉及到数据共享的问题,故这里用到了线程同步的知识。 package xiaojie; public class Maipiao { public stat...
  • qq_32079585
  • qq_32079585
  • 2016年05月31日 18:34
  • 888

《线程》——多线程同步实例剖析

线程这个名词我们在学习操作系统的时候就接触过了,线程又称为轻量级进程,那进程是什么哪?大家可以跟随我的超链接看一下百度百科的解释。      简单线程实例,解决两个售票窗口售票问题。      具体的...
  • u013067402
  • u013067402
  • 2016年02月15日 08:16
  • 1496

C++线程同步方式及例子

C++线程同步方式及例子
  • daoming1112
  • daoming1112
  • 2017年01月24日 22:07
  • 427

线程同步和异步 实例(一)

using System; using System.Collections.Generic; using System.Linq; using System.Text; /* * 同步方...
  • jack15850798154
  • jack15850798154
  • 2011年09月08日 09:08
  • 1463

C++ 之 经典线程同步案例之多线程(五)

首先在此感谢 MoreWindows 秒杀多线程面试题系列让我成长和学习,同时也借鉴了很多优秀观点和示例! 在此也借鉴MoreWindows案例加以说明:   程序描述: 主线程启动10个子线程并将...
  • u010236550
  • u010236550
  • 2013年12月18日 15:32
  • 1419

线程同步的小例子

// example.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include #include #include usin...
  • qingzai_
  • qingzai_
  • 2016年06月21日 23:45
  • 536

线程同步异步实例分析

我的理解: 同步就是顺序执行。 异步就是可以并行执行。 多线程是实现异步操作的一种手段或方式...
  • fangqun663775
  • fangqun663775
  • 2015年06月12日 11:16
  • 599

Servlet 监听器实例

Servlet监听器实例
  • cjc211322
  • cjc211322
  • 2014年11月26日 10:14
  • 1262

Servlet简单实例

使用netbeans开发:1 创建servletTest.java:/**//* * servletTest.java * * Created on 2008年4月18日, 上午9:32 */pack...
  • longronglin
  • longronglin
  • 2008年04月18日 09:46
  • 1906

一个servlet可以创建一个或者多个实例对象吗?

在面试时,对于初学者来说,这不是一个简单的问题,没有太多的工作经验,这题你是不敢确认的,因为平常谁会想到这样的怪问题呢?    甚至很多工作了几年的程序员也不知道,因为他们没遇到过这样的问题。所以说这...
  • qwwspring
  • qwwspring
  • 2007年05月19日 19:26
  • 885
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Servlet线程同步实例研究
举报原因:
原因补充:

(最多只允许输入30个字)