TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
通俗地讲,JSP就是内嵌java(脚本)代码的网页。具体到实际系统环境中,当客服端通过浏览器请求一个JSP页面时,JSP页面不会原封不动的传送给浏览器,它将首先被服务器编译处理成为一个Servlet,然后执行这个servlet来完成浏览器的请求。因此,就决定了JSP具有Servlet的特性(服务器端特性) 和Web特性(客户端特性)。在默认条件下,Servlet/JSP是以多线程模式执行的,当然也允许多个客户端同时访问、获取一个JSP页面。在实践中对应于这两种特性也就有不同的JSP同步需求。我们现在就分别讨论之。 一、对于某一时刻要求实施同步技术的JSP页面
"一层"架构系统中较容易碰到,比如在一个系统中通过一个JSP页面去读并且写服务器硬盘文件,这时就需要同步JSP页面,否则就会产生混乱。为了避免出现同一刻有多用户同时访问一个JSP页面,在JSP规范中通过指令元素isTheadSafe 来设定JSP是否支持多线程(默认是支持多线程的),在JSP中加入如下语句可以关闭JSP的多线程支持:<%@ page isTheadSafe="true"%>(在网上看到srx81兄在《Servlet及JSP中的多线程同步问题》谈到在Tomcat 4.1.17版本中不支持这一指令元素的BUG,本人没有弄到这一版本的tomcat,未能测试,希望大家在实践时多加注意)。由于对应的是JSP的servlet特性,当然也可以采用其它的Java同步方法来进行单线程保护,比如在脚本元素中使用synchronized关键字进行同步。 使用此类方法实现JSP页面同步需要注意由于这一方法只是在某一时刻JSP的同步,所以使用涉及到于整个page范围的实例变量与页面元素时要遵守"用前打招呼(声明),用完洗干净(释放)"的原则,如下:
例一: <%@ page contentType="text/HTML;charset=GBK" %>
<%@ page isTheadSafe="true"%>
<%
java.io.PrintWriter output; //定义并引用实例变量
String username = request.getParameter("username");
String password= request.getParameter("password");
%>
<%
//打印变量
output.println("username:" + username + "<br>");
output.println("password:" + password + "<br>");
%> 例二:
<%@ page contentType="text/html;charset=GBK" %>
<%@ page isTheadSafe="true"%>
<%
java.io.PrintWriter output; //声明实例变量
String username = null;
String password= null;
%>
<%
//引用变量
username = request.getParameter("username");
password = request.getParameter("password"); //打印变量
output.println("username:" + username + "<br>");
output.println("password:" + password + "<br>"); //释放变量
username = null;
password= null;
%>
在例一中由于对非同步的JSP实例变量处理不够彻底,所以在实际应用中虽然可以对JSP实例变量的操作实现同步,但如srx81兄所言, "并不能阻止一个线程使用另外一个线程修改后的"脏的"实例变量"。 实践中,如例二这样处理后已经完全可以满足某一时刻对JSP页面的同步。 补充几句,具体到一个系统中,如果要求同步的还有通过JSP传递出去的参数,就要全盘考虑整个架构中的同步与多线程问题(比如采用同步的hastable来进行层间数据传输),此外还要总体考虑同步带来的"瓶颈"与效率低下问题。 二、对于某一时段内要求实施同步技术的JSP页面 这是对应于JSP的Web特性的,即指同一时段内只能有一个客户端访问该JSP页面。简单说来就是当有一人访问某一JSP页面时,其他人就不能访问这一JSP页面,情况很类似于数据独占和数据锁。这种情况在大多系统开发中都可能碰到。具体说来又细分为两种情形: A、涉及数据更新的JSP页面同步
此类情况主要考虑的就是数据的安全性,所以一般就直接套用数据并发策略(如数据锁),但是要注意目前是基于JSP的Web特性来实施同步的,对应Web特性有两个问题:Web的不稳定性、Web与服务器的弱耦合通讯。如果直接使用数据锁方案就会出现问题,我们可以设想,有一个用户进入JSP页面,同时触发同步的数据锁,此后的所有用户都不能在进入到此JSP页面(因为得不到数据),直到该用户离开此JSP页面,再触发条件释放数据锁。但是如果这个用户掉线或者当机了,那个释放数据锁的触发条件将不会传给服务器,那个数据锁就会永远不被释放,形成可怕的死锁。解决的方法当然很多,比如定时检测数据锁的占用情况,并释放不在线用户占用的数据锁。 就个人实践来说,我更喜欢采用"版本同步"的方案,这一方案相较而言更安全简单。简单说来就是在数据表中添加一个"版本号"字段,用户进入JSP页面时同时读取当前记录"版本号"字段,进行更新操作时除了要满足正常条件外还需要满足先前取出的"版本号"与数据库记录中存储的版本号相一致,完成更新后同时将版本号也更新(可以用时间来标识,也可以用修改的此书来标识)。这样,同时段内进入JSP页面的用户中只有一人(第一个完成更新的用户)能实现数据的更新,从而达到"准同步"的目的。 这一方案去掉了数据死锁的问题,但也有缺点,那就是因"手慢"而未能实现更新操作的用户会抱怨由于"准同步"的缘故,导致他浪费时间去做无用的更新操作(版本过期),因此这一方案适用于要求数据线程安全,同时可能执行更新操作的线程不多、更新数据量不大的系统功能中。 B、不涉及数据更新的JSP页面同步 这类情况要求的就是实现"JSP页面同步锁",效果是一个用户进入JSP页面后,就触发"JSP页面同步锁",其他用户就不能再进入该JSP页面,直到该用户离开后触发条件释放"JSP页面同步锁"。这几句�嗦的描述与数据锁地描述基本相同,关键的问题是要你自己去开发实现一个"JSP页面同步锁"。 实践中,我们选用ServletContext对象来充当"JSP页面同步锁",因为它满足"在应用程序的整个生命周期间都有效且放在这个对象内的数据任何Web组件都能访问到",ServletContext被认为是对于Web应用软件的一个整体性存储区域(每一个Web应用软件都具有ServletContext)。存储在ServletContext之中的对象将一直被保留,除非是被删除。见下例: 进入需要同步的JSP页面前先要访问"锁"lockservlet.class,并传递访问者的账号做为开锁与闭锁的钥匙,其中主要代码如下: 访问lockservlet进入同步JSP并触发关闭同步锁:
http://localhost/lockservlet?key=sa¶m=close 关闭"JSP页面同步锁"。 例3: ...
public void service (HttpServletRequest req,HttpServetReponse res) throws ServletException,IoException{
//初始化参数
ServletContext context=getServletContext(); String key =(String) req.getParameter("key");
//获取同步锁状态
String lock=(String) context.getAttribute("S_LOCK"); //进入进入同步JSP并触发关闭同步锁
if ("close ".equals(req.getParameter("param")))
//同部锁尚未关闭
if(lock==null){
//关闭同步锁
context.setAttribute("S_LOCK", key);
//进入同步JSP
res.sendRedirect("synchronization.jsp");
//如果同部锁已被关闭
}else{
//判断该锁是否有效关闭
//(检查占锁的是否在线用户,所有在线用户都登记在表User_online中)
<本段代码略>
//如果是在线用户的有效关锁,返回初始页面
res.sendRedirect("first.jsp");
//如果是非在线用户的无效关锁
//关闭同步锁
context.setAttribute("S_LOCK", key);
//进入同步JSP
res.sendRedirect("synchronization.jsp");
}
}
... 当离开同步保护的JSP页面时,访问lockservlet并触发打开同步锁:
http://localhost/lockservlet?param=open 代码段如下: ...
if ("open".equals(req.getParameter("param"))){
//打开同步锁
context.setAttribute("S_LOCK",null);
...
}
... 在实践中,最好对lockservlet的代码也采取同步。 关于JSP实现同步的技术与实践,个人总结归纳如上,在J2EE强大的技术体系中,还可以采用诸多方法来解决之,希望各位同道多多交流探讨。 作者简介 张劲松,1996年大学毕业,在某研究所工作,其间一直参与IT方面的工作。自2001年进入云电清华同方科技股份有限公司以来,参与了多个大型项目开发,一直使用并极力支持J2EE架构下的项目开发,对J2EE架构下项目开发中的诸多技术细节和各种开发模式均有较为丰富的实践和心得,对于J2EE架构下项目开发的架构设计、实施成本、技术评估亦有较好的把握,拥有SCJP认证。 |
|