对于servlet的线程安全问题为何会出现呢?从前面的servlet的生命周期的学习我们知道,由于通常情况下,一个Servlet在内存只有一个实例处理请求,当多个请求发送过来的时候就会有多个线程操作该servlet对象,此时可能导致线程安全问题。
下面我们首先通过两个例子来进行一下对比来引出线程安全问题的讨论。
首先,看Demo1,为了更好的说明问题,测试的时候我用两个浏览器去访问同一个Servlet(模拟多个用户访问的case)
package com.jjyy.servlet.thread;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* servlet线程安全问题分析 --Demo1
* @author JiangYu
*
*/
public class ServletThreadSaveAnalysis extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int i = 0;
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("测试结果:"+(++i));
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
结果如下:
先不做解释,我们在看第二个例子:代码如下
package com.jjyy.servlet.thread;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* servlet线程安全问题分析 --Demo2
* @author JiangYu
*
*/
public class ServletThreadSaveAnalysis extends HttpServlet {
int i = 0;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("测试结果:"+(++i));
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
同样,我开启两个浏览器同时去访问,结果如下:
我再次刷新右边的浏览器,结果如下:
怎么回事,一下子就从12变成了23。到此,问题就出现了。那么为何会出现这样的情况呢?我们可以对比上面的两段代码,唯一不同之处就是,第一个中的i是局部变量,二个程序中的i是一个全局变量,所以应该知道问题的原因了吧!
由于通常情况下,一个Servlet在内存只有一个实例处理请求,当多个请求发送过来的时候就会有多个线程操作该servlet对象,这里我们模拟的是两个用户来回的刷新浏览器,当servlet中存在全局变量的时候,自然每次请求都会对其进行改变,而对于局部变量则不会存在这样的问题。
既然存在问题,我们猜肯定有解决的方法:下面提供几种供参考的解决方法
(1)利用同步代码块解决问题。
package com.jjyy.servlet.thread;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* servlet线程安全问题分析 --Demo3
* @author JiangYu
*
*/
public class ServletThreadSaveAnalysis extends HttpServlet {
int i = 0;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//加锁的方式
synchronized (this) {
try {
//模拟延迟
Thread.sleep(4000);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("测试结果:"+(++i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
虽然加锁可以防止问题的发生,但是如果是高并发量访问的情况下,每一个请求都要持续四秒的时间的话,第二个就要等8秒,第三个就要等12秒,……,我们现在是模拟的4秒延迟,但是实际情况中,如果一个人来访问就加上锁的话,如果他一直不释放锁,那么可想而知这是行不通的。所以说加锁方式缺陷是,同一时间同步代码块只能处理一个请求,效率很低下,所以同步代码块中尽量只包含核心的导致线程安全问题的代码。
(2)为该servlet实现SingleThreadModel接口
package com.jjyy.servlet.thread;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* servlet线程安全问题分析 --Demo4
* @author JiangYu
*
*/
public class ServletThreadSaveAnalysis extends HttpServlet implements SingleThreadModel {
int i = 0;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("测试结果:"+(++i));
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
此为一个标记接口,被标记的servlet将会在内存中保存一个servlet池,如果一个线程来了而池中没有servlet对象处理,则创建一个新的。如果池中有空闲的servlet则直接使用。为什么会存在问题呢?首先,还是上面的代码,假设我们有两个浏览器同时第一次来请求这个Servlet,这两个线程都到servlet池中看一下,发现没有servlet对象,就都各自创建了一个servlet对象,当使用完之后都放回池中,此时如果有第三个线程来访问,它随便取哪一个都是输出2,它使用完后,将servlet放入池中,此时有第4个线程来访问,第一次可能输出3,第二次可能输出2,为何,因为它第二次访问的时候可能拿到那个值为1的servlet对象,这是就会出现问题了。所以,SingleThreadModel接口并不能真的解决线程安全问题。并且此接口已经被废弃。
由于默认情况下Servlet在内存中只有一个对象,当多个浏览器并发访问Servlet时就有可能产生线程安全问题,综上分析,
1.加锁--效率降低
2.SingleThreadModel接口--不能真的防止线程安全问题
最终解决方案:
在Servlet中尽量少用类变量,如果一定要用类变量则用锁来防止线程安全问题,但是要注意锁住内容应该是造成线程安全问题的核心代码,尽量的少锁主内容,减少等待时间提高servlet的响应速度。
分享到:
相关推荐
**JSP、Servlet与Filter详解** 在Web开发领域,Java技术是不可或缺的一部分,其中JSP(JavaServer Pages)...在实际项目中,合理利用Filter可以提高应用的安全性和性能,而JSP和Servlet则负责处理用户交互和业务逻辑。
5. **挑战与机遇**:5G的发展也面临挑战,如频谱资源紧张、网络安全风险增加、设备能耗问题等。但这些挑战也为技术创新提供了机会,如研发更高效的频谱利用技术、强化网络安全防护、优化设备能效等。 6. **未来展望...
通信行业:温故知新,从4G看5G-1202-中信建投-12页.pdf
农林牧渔行业专题研究:温故知新,复盘上两轮生猪疫情影响-0213-广发证券-17页.pdf
ZYNQ7100,PL 纯逻辑,简单入门 FPGA LED 例程 工程对应的视频教程(1.1 ZYNQ学习分享-温故知新-PL-led):https://www.ixigua.com/6878303436048335371/
这篇报告是光大证券“类固收系列报告”的第四部分,旨在为机构投资者提供参与科创板新股网下配售的指导,通过科学的模型和策略提高投资收益。之前的报告分别讨论了打新收益的预期、市场热度、战略配售基金的投资价值...
工业机器人是现代制造业中不可或缺的重要组成部分,其发展水平是衡量一个国家制造业自动化、智能化程度的重要标志。本文档探讨了工业机器人在汽车行业中的应用,并结合美国汽车行业历史上的“2mm工程”,分析了工业...
纺织服装行业“温故知新”系列之百丽复盘:渠道为王铸就龙头,改革初见成效 纺织服装行业是一个复杂且多样化的行业,涉及到多个领域,如女鞋、运动、服饰等。百丽作为行业龙头,凭借“多品牌+快速渠道扩张+强供应链...
这是关于并列与分布式的技术文档的一部分,Processes or Threads 线程与进程,本开发文档适合对于并列与分布式感兴趣的有一定计算机基础初学的朋友,一个快捷的学习文档或者温故知新,这是第一部分不要忘了第二部分...
- **复习提问**:回顾上节课内容,温故知新。 - **精读感悟**: - **分析人物**:通过具体语句分析爱因斯坦的性格特征。 - **讨论活动**:组织学生围绕“爱因斯坦是不是‘最伟大的人’”展开讨论,强调证据支持...
ASP.NET 2.0是微软开发的一个用于构建Web应用程序的框架,它基于.NET Framework,提供了丰富的功能和强大的性能。...同时,也可以从中学习到如何将这些知识应用于实际项目,解决实际问题,提升开发技能。
这篇《四年级语文上册 第五单元 16 麻雀教案 新人教版五四制》是一个针对小学四年级学生的语文教学方案,主要涉及的是对《麻雀》这篇课文的教学设计。课文内容围绕一只老麻雀在面对猎狗威胁时,展现出的伟大母爱。 ...
同时,5G的发展也引发了对网络安全、隐私保护和频谱资源分配等问题的关注。 总结来说,《中信建设温故知新,从 4G 看 5G》的资料深入浅出地解析了4G与5G之间的差异,展示了5G如何在速度、容量、延迟等方面超越4G,...
而10-2Y的曲线交易因两年期国债期货的高波动性和流动性问题,表现更加不稳定。 3. **市场环境与影响因素** - 市场数据如中债综合指数、银行间国债收益率等指标反映了债券市场的整体态势,企业/公司/转债规模则揭示...
在“温故知新”的标签下,这个问题提醒我们回顾和巩固Android的通话服务和数据库操作的知识,同时也需要了解VoWifi技术及其在Android生态中的实现。通过对`CallLogManager.java`的深入理解和定制,我们可以确保...
初中语文文学讨论现当代文学温故知新
- 日常安全管理:对外来人员、幼儿接送、活动环节进行严格监管,每日巡查,及时解决问题,避免安全隐患。 - 平安教育:定期开展安全教育活动,加强师生和家长的安全意识。 - 技防与物防结合:利用监控系统24小时...
【知识点详解】 1. 单元主题:Unit 3的主题是"Is it ..."主要围绕天气相关词汇和句型展开,通过多种教学手段帮助四年级学生掌握这些基础的英语表达,提升他们的听说读写技能,并培养他们对自然环境的关注和爱护。