设为首页 收藏本站
查看: 518|回复: 0

[经验分享] 管中窥豹,结合多线程,对于tomcat中servlet加载的一点试探

[复制链接]

尚未签到

发表于 2017-2-9 09:58:48 | 显示全部楼层 |阅读模式
  对于Servlet自己的生命周期我们这里不谈了,本文主要想测试一下Tomcat中结合多线程,Servlet实例化过程是怎样的。
  写第一个demo servlet

public class DemoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private String time = "first";
@Override
public void init() throws ServletException {
System.out.println("DemoServlet.init()");
}
@Override
public void destroy() {
System.out.println("DemoServlet.destroy()");
}
public DemoServlet() {
System.out.println("DemoServlet.DemoServlet()");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(Thread.currentThread().getId());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
response.getWriter().println("<html><body><h3>Welcome "+time+"</h3></body></html>");
time = "second";
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

   连续访问 http://localhost:8080/test-servlet/DemoServlet 两次,
  console里得到如下的输出:
  DemoServlet.DemoServlet()

DemoServlet.init()

18

18

浏览器里的两个页面得到的输出分别是:

Welcome first
  和

Welcome second
  分析一下:
  1. console里DemoServlet.init()和DemoServlet.DemoServlet()都分别只打印了一次,说明Servlet只实例化了一次,我们在浏览器里看到的情况也应证了这一点,这个Servlet里的time变量是同一个。
  2. console里的18是当前线程的ID,因为线程的ID在同一个jvm里是唯一的,所以这两次访问是同一个线程。而浏览器打开页面的响应时间也正好证明了这一个,第一个页面消耗量大约5s,第二个页面大约消耗量10s,因为是同一个线程,所以要排队等待。
  分析到这里我有一点收获就是我之前yy的Tomcat决定何时开启一个新的线程应该是要看当前池里有没有空闲线程,如果没有那么要新开一个,从这里我看不是,因为明显这唯一的线程当时卡在了doGet方法里,表现为是忙碌的。
  再往下想,因为是在同一个线程里那么Servlet实例只有一个也是理所当然了。
  这时我很好奇,何时才开启新的线程呢?google里一下,发现有人这么测试,说是测出多线程了,我也测了一下,并加入了线程ID的打印语句,发现确实是多线程

public class DemoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void init() throws ServletException {
System.out.println("DemoServlet.init()");
}
@Override
public void destroy() {
System.out.println("DemoServlet.destroy()");
}
public DemoServlet() {
System.out.println("DemoServlet.DemoServlet()");
}
PrintWriter output;
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(Thread.currentThread().getId());
String username;
response.setContentType("text/html; charset=gb2312");
username = request.getParameter("username");
output = response.getWriter();
try {
Thread.sleep(5000); // 为了突出并发问题,在这设置一个延时
} catch (InterruptedException e) {
}
output.println(" 用户名:" + username + "<BR>");
}
}

  用浏览器同时访问  http://localhost:8080/test-servlet/DemoServlet?username=a  和  http://localhost:8080/test-servlet/DemoServlet?username=b
  得到console里的输出:

DemoServlet.DemoServlet()

DemoServlet.init()

18

19


再看浏览器:
  第一个页面是空白
  第二个页面是
用户名:a

用户名:b
  是不是有点奇怪呢,怎么第一个页面的内容跑到第二个页面里来了?分析一下就清楚了,首先看两个线程ID不同,很显然是开启了新的线程了。而init只调用了一次,说明使用的是同一个Servlet实例,进而我们知道output成员变量肯定也是同一个引用了,那么再两个线程里同时交叉执行了service方法。
  说到这里我先插一句:在两个线程中同一实例的成员变量也是同一个,而在方法里的临时变量却是不同的,临时的嘛,虽然是同一个方法,但在不同的执行环境下当然就是两个东西了。
  好了,再分析service方法的执行。第一个线程先执行,得到用户名为"a",并将output引用指向了当前response的输出流,然后他睡觉去了。这时候第二个线程又开始执行service,那么得到了一个新的用户名为"b",注意前面说过了这是临时变量,所以前面那个用户名没有被覆盖,还是"a",然后又将output引用指向了新的实例,因为这个引用是同一个,所以前面的引用就被覆盖了,然后他也睡觉去了。5s后第一个线程睡醒了,他向output里写东西,但是这时output已经不是与第一个浏览器的连接中的输出流,而是写到第二个浏览器里去了,当然他写的还是"a"。紧接着第二个线程也睡醒了,他将"b"也写到这个浏览器里去了。
  结果是第一个浏览器啥也没得到,而第二个浏览器却得到了俩。
  说到这里还有一个问题没有解决,为什么我第一次测试tomcat只启动了一个线程,而第二次却启动了两个呢?很奇怪,所以我又把代码后撤回去,回到第一个demo,又测试了两个页面,但这次测试的两个地址是第二次测试的地址,就是带上两个不同的参数。OK,这次跟奇怪了,输出结果不写了,只说我发现这次奇迹般的开了两个线程!
  到这是我突然明白了,tomcat是这样决定,至少在本次测试的情况下是这样决定何时开启新的线程的:相同的URL就还用原来的线程,不管忙不忙;URL不同就新开一个线程。因为tomcat任务这个请求的URL不同那么就是带有不同的状态(我是这么理解的)。然后又测试了一下两组地址,更验证了以上的推理。
  http://localhost:8080/test-servlet/DemoServlet?username=a和http://localhost:8080/test-servlet/DemoServlet?username=a
  >>>同一个线程
  http://localhost:8080/test-servlet/DemoServlet?username=a和http://localhost:8080/test-servlet/DemoServlet?username=a&userid=c
  >>>不同线程
  总结一下:
  1. tomcat根据是否是同一URL来判断是否开启一个新的线程。
  2. 不同请求,如果是同一线程,Servlet实例是同一个
  3. 不同请求,如果是不同线程,Servlet实例是也同一个
  但以上次总结仅限于我测试的情况,很可能在更加复杂的情况下tomcat会有更加复杂的处理和变化。
比如在并发量很大的时候以上的第1条可能就不成立了,可能还要考虑到队列的长度等问题。又比如当考虑了session的情况下第2和3条也许也不成立了。
  这些问题以后再详细研究,最好是能跟一下tomcat源代码,才能最终弄清楚。
  最后,有错误请大家指出,没有错误请大家补充。
  

  本人新博客:tuoxie.me

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-339599-1-1.html 上篇帖子: Out of memory Error:JAVA ;Out of memory ;Tomcat; PermGen space 下篇帖子: Eclipse3.6中Tomcat发布时间异常的Bug 2010-9-8修复链接地址
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

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