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

相关文章推荐

线程同步编程实例

  • 2012年04月13日 10:32
  • 1.14MB
  • 下载

java线程同步实例

  • 2011年11月14日 15:15
  • 4KB
  • 下载

Java多线程技术研究(二)-线程同步,通信及ThreadLocal

本篇博客研究线程同步,线程通信,以及ThreadLocal。 一、线程同步 线程安全 线程不安全 可见性 不可见性 二、线程通信 三、ThreadLocal(线程本地变量) 在多线程开...

深入研究java的join使线程同步

/** * join可以使线程同步 * @author huawangxin * */ public class ThreadDemo { //表示图盘是否下载完毕 public stat...

Java线程同步实例

Java线程同步实例 实例1 public class mytread { public static void main(String[] args) { new Crea...
  • tzh476
  • tzh476
  • 2016年11月03日 21:50
  • 284

java 线程同步实例

一个简单的java线程同步例子。 package com.easecom.zsk.web; public class ThreadDemo { /** * @param args ...

Java线程同步实例 生产者 消费者问题分析

Java线程同步实例 生产者 消费者问题分析 实例1 public class mytread { public static void main(String[] args) { ...
  • tzh476
  • tzh476
  • 2016年11月01日 16:20
  • 256

利用互斥对象实现线程同步的实例说明

多线程编程中,如果我们稍有不注意,很容易出现一些意想不到的结果,主要原因就是多线程的同步处理;我们需要保证多个线程在共同运行时,进行对应资源的同步处理,保证一个线程访问共享资源时,其他线程不能访问该资...

线程同步异步实例分析

我的理解: 同步就是顺序执行。 异步就是可以并行执行。 多线程是实现异步操作的一种手段或方式...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Servlet线程同步实例研究
举报原因:
原因补充:

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