在Java的server side开发过程中,线程安全(Thread Safe)是一个尤为突出的问题。
因为容器,如Servlet、EJB等一般都是多线程运行的。虽然在开发过程中,我们一般
不考虑这些问题,但诊断问题(Robust),程序优化(Performance),我们必须深入它们。
什么是线程安全?
引用
Thread-safe describes a program portion or routine that can be called from multiple programming
threads without unwanted interaction between the threads。
在Java里,线程安全一般体现在两个方面:
1、多个thread对同一个java实例的访问(read和modify)不会相互干扰,它主要体现在关键字
synchronized。如 ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized
关键字)。如果你在 interator一个List对象时,其它线程remove一个element,问题就出现了。
2、每个线程都有自己的字段,而不会在多个线程之间共享。它主要体现在java.lang.ThreadLocal类
,而没有Java关键字支持,如像static、transient那样。
一个普遍的疑问,我们的Servlet中能够像JavaBean那样declare instance或static字段吗?如果不可以?
会引发什么问题?
答案是:不可以。我们下面以实例讲解:
首先,我们写一个普通的Servlet,里面有instance字段count:
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),所有的并发请求都要排队!
分享到:
相关推荐
ZZ公司针对安全生产制定了一系列守则,不仅体现了对安全生产重要性的认知,而且展现了公司科学管理和法制观念的运用。本文将深入探讨ZZ公司安全生产守则的核心内容及其实践意义。 首先,安全生产的重要性是 ZZ 公司...
base zz zz zz zz zz base zz zz zz zz zz base zz zz zz zz zz base zz zz zz zz zz
【网络安全竞赛概述】 全国职业院校技能大赛(中职组)的网络安全竞赛旨在考察参赛选手对网络安全基础知识的理解和实践操作能力。比赛分为四个模块:基础设施设置与安全加固、网络安全事件响应、数字取证调查和应用...
zz中职网络安全逆向题目
本份文档是一份关于全国职业院校技能大赛中的一项赛项规程的文件,具体为网络安全赛项,编号为ZZ-2021029。这一赛项旨在检验中职学校网络信息安全专业的教学成效,并促进该专业的教学改革。同时,通过大赛培养符合...
在IT领域,网络编程是不可或缺的一部分,而TCP(Transmission Control Protocol)作为一种面向连接的、可靠的传输...在实际开发中,还需要注意线程安全、线程池的使用以及资源管理等问题,以确保服务器的稳定性和效率。
非常感谢您对ZZ的信任。信任就像一道桥梁,将您和ZZ... 为此,ZZ工作室为确保大家的隐私不被泄露,让电脑更安全,从而研发了ZZ安全盾甲安全软件,无时无刻保护着您的电脑不受侵害。ZZ安全盾甲将拼了命的保护您的电脑。
**Qt多线程基础** Qt库提供了一套强大的多线程机制,允许开发者在应用程序中同时执行多个任务,提高程序的响应性和效率。在给定的"Qt多线程简单实例"中,我们关注的核心是如何创建和管理线程,以及如何通过用户交互...
2021zz网络安全重庆市赛
访问路径为:http://localhost:8080/web001/test.do?flag=xxyyzz&switch=off 其中switch开关参数取值有两种:on和off,on表示执行线程对应的任务,off表示中断线程正在执行的任务。
全国职业院校技能大赛的网络安全竞赛旨在考察参赛者在网络安全领域的综合技能,包括基础设施设置与安全加固、网络安全事件响应、数字取证调查、应用安全以及CTF(Capture The Flag)攻防演练。比赛分为四个模块,...
2021年网络空间安全国赛题目解析第一套
中职组网络安全国赛试题1
网络安全赛项赛题 中职赛项 适合正在准备技能大赛的人群
ZZ561401.CAB ZZ561401.CAB ZZ561401.CAB
最后,为了优化用户体验,开发者可能运用了异步处理技术,如AsyncTask或IntentService,避免UI线程阻塞,确保应用流畅运行。同时,通知服务也可能被用来提醒用户重要的医疗信息或待办事项。 总的来说,“zz-doctor...
wincc SIMATIC WinCC是第一个使用最新的32位技术的过程监视系统,具有良好的开放性和灵活性。 从面市伊始,用户就对SIMATIC WinCC印象深刻。
网络安全比赛试题
Java线程池是一种高效管理线程资源的工具,它的设计思想是基于生产者消费者模型,借鉴了工厂模式和代理模式的元素。线程池通过维护一组可重用线程,减少了创建和销毁线程的开销,提高了系统响应速度与并发处理能力。...
ZZ-2021030 网络搭建与应用赛项赛卷-网络搭建及安全部署竞赛报告单