在看zwchen很久之前的一篇博客,地址如下
http://zwchen.iteye.com/blog/91088
做了个测试,按照第一种做法
为使用synchronized,并未出现 线程共享情况,(我使用的是tomcat6.0带的servlet包)数据都是
SimpleServlet@c4fe76 ==> Thread[http-8080-1,5,main]:
Counter =
60
Counter = 61
Counter = 62
Counter = 63
Counter = 64
Counter =
65
Counter = 66
Counter = 67
Counter = 68
Counter = 69
SimpleServlet@c4fe76 ==> Thread[http-8080-1,5,main]:
Counter =
70
Counter = 71
Counter = 72
Counter = 73
Counter = 74
Counter =
75
Counter = 76
Counter = 77
Counter = 78
Counter = 79
SimpleServlet@c4fe76 ==> Thread[http-8080-1,5,main]:
Counter =
80
Counter = 81
Counter = 82
Counter = 83
Counter = 84
Counter =
85
Counter = 86
Counter = 87
Counter = 88
Counter = 89
正常输出。 请问各位高手,这个是为什么??
还有,其中的
synchronized (mutex) 为什么要用mutex这个参数?
我认为synchronized的参数是表明同步的是哪块,所以我改成 我自己servlet中的req,也可以正常跑
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
synchronized (req)
另外 ,最关键的:Thread.sleep((long) Math.random() * 1000);
我改成Thread.sleep(1000);,输出的结果就好像是线程共享了??
SimpleServlet@64883c ==> Thread[http-8080-3,5,main]:
Counter =
4
Counter = 6
Counter = 10
Counter = 14
Counter = 18
Counter =
22
Counter = 26
Counter = 30
Counter = 34
Counter = 38
SimpleServlet@64883c ==> Thread[http-8080-6,5,main]:
Counter =
4
Counter = 7
Counter = 11
Counter = 15
Counter = 19
Counter =
23
Counter = 27
Counter = 31
Counter = 35
Counter = 39
SimpleServlet@64883c ==> Thread[http-8080-7,5,main]:
Counter =
4
Counter = 8
Counter = 12
Counter = 16
Counter = 20
Counter =
24
Counter = 28
Counter = 32
Counter = 36
Counter = 40
下面附上 ZWCHEN先生的原文,并对他表示深深的敬意,谢谢!
- public class SimpleServlet extends HttpServlet
- {
- // A variable that is NOT thread-safe!
- private int counter = 0;
- public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
- {
- doPost(req, resp);
- }
- public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
- {
- resp.getWriter().println("<HTML><BODY>");
- resp.getWriter().println(this + " ==> ");
- resp.getWriter().println(Thread.currentThread() + ": <br>");
- for (int c = 0; c < 10; c++)
- {
- resp.getWriter().println("Counter = " + counter + "<BR>");
- try
- {
- Thread.sleep((long) Math.random() * 1000);
- counter++;
- }
- catch (InterruptedException exc)
- {
- }
- }
- resp.getWriter().println("</BODY></HTML>");
- }
- }
然后,我们通过一个html页面向该servlet发出三次请求:
- <HTML>
- <BODY>
- <TABLE>
- <TR>
- <TD><IFRAME src="./SimpleServlet" name="servlet1" height="200%"> </IFRAME></TD>
- </TR>
- <TR>
- <TD><IFRAME src="./SimpleServlet" name="servlet2" height="200%"> </IFRAME></TD>
- </TR>
- <TR>
- <TD><IFRAME src="./SimpleServlet" name="servlet3" height="200%"> </IFRAME></TD>
- </TR>
- </TABLE>
- </BODY>
- </HTML>
刷新页面几次后,产生的结果为:
com.zwchen.servlet.SimpleServlet@11e1bbf ==> Thread[http-8081-Processor23,5,main]:
Counter = 60
Counter = 61
Counter = 62
Counter = 65
Counter = 68
Counter = 71
Counter = 74
Counter = 77
Counter = 80
Counter = 83
com.zwchen.servlet.SimpleServlet@11e1bbf ==> Thread[http-8081-Processor22,5,main]:
Counter = 61
Counter = 63
Counter = 66
Counter = 69
Counter = 72
Counter = 75
Counter = 78
Counter = 81
Counter = 84
Counter = 87
com.zwchen.servlet.SimpleServlet@11e1bbf ==> Thread[http-8081-Processor24,5,main]:
Counter = 61
Counter = 64
Counter = 67
Counter = 70
Counter = 73
Counter = 76
Counter = 79
Counter = 82
Counter = 85
Counter = 88
我们会发现三点:
servlet只产生了一个Servlet对象,因为输出this时,其hashcode都一样,
servlet在不同的线程(线程池)中运行,如http-8081-Processor22,http-8081-Processor23
Count被这三个doGet方法共享,并且并行修改。
上面的结果,违反了线程安全的两个方面。
那么,我们怎样保证按照我们期望的结果运行呢?首先,我想保证产生的count都是顺序执行的。
我们将Servlet代码重构如下:
- public class SimpleServlet extends HttpServlet
- {
- //A variable that is NOT thread-safe!
- private int counter = 0;
- private String mutex = "";
- public void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException
- {
- doPost(req, resp);
- }
- public void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException
- {
- resp.getWriter().println("<HTML><BODY>");
- resp.getWriter().println(this + ": <br>");
- synchronized (mutex)
- {
- for (int c = 0; c < 10; c++)
- {
- resp.getWriter().println("Counter = " + counter + "<BR>");
- try
- {
- Thread.sleep((long) Math.random() * 1000);
- counter++;
- }
- catch (InterruptedException exc) { }
- }
- }
- resp.getWriter().println("</BODY></HTML>");
- }
- }
我们的输出结果为:
com.zwchen.servlet.SimpleServlet@109da93:
Counter = 0
Counter = 1
Counter = 2
Counter = 3
Counter = 4
Counter = 5
Counter = 6
Counter = 7
Counter = 8
Counter = 9
com.zwchen.servlet.SimpleServlet@109da93:
Counter = 10
Counter = 11
Counter = 12
Counter = 13
Counter = 14
Counter = 15
Counter = 16
Counter = 17
Counter = 18
Counter = 19
com.zwchen.servlet.SimpleServlet@109da93:
Counter = 20
Counter = 21
Counter = 22
Counter = 23
Counter = 24
Counter = 25
Counter = 26
Counter = 27
Counter = 28
Counter = 29
这符合了我们的要求,输出都是按顺序的,这正式synchronized的含义。
附带说一下,我现在synchronized的是一个字符串变量mutex,不是this对象,这主要是从performance和Scalability考虑。Synchronized用在this对象上,会带来严重的可伸缩性的问题(Scalability),所有的并发请求都要排队!