1.线程安全的定义
在《java并发编程实战》给出的定义为:“当多个线程访问某个类时,不管运行时环境采用何种方式调度,或者这些线程如何交替运行,并且在主调函数中不需要额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类为线程安全的”
2.servlet的调用过程和生命周期
servlet的生命周期
servlet是运行咋servlet容器中由容器来管理,常用的servlet容器是Tomcat,Jboss,weblogic。servlet的生命周期有四个阶段,分别为:加载、实例化,请求处理、销毁。通过java。servlet。Servlet接口中的init(),service()和destory()表示。
加载并实例化
Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,Servelt通过类加载器来加载Servlet类,加载完成后再new一个Servlet对象来完成实例化。
请求处理
在Servlet初始化后,容器就可以准备处理客户机请求了。当容器收到对这一Servlet的请求,就调用Servlet的service()方法,并把请求和响应对象作为参数传递。当并行的请求到来时,多个service()方法能同时运行在独立的线程中。通过分析ServeltRequest或者HttpServletRequest对象,service()方法能处理用户请求,并调用ServletRequestResponse或者HttpServletRequestResponse对象来响应。
销毁
一旦Servlet容器检测到一个Servlet要被卸载,这可能是因为要回收资源或者因为它正在被关闭,容器会在所有Servlet的service()线程之后,抵用Servlet的destory()方法。然后,Servlet就可以进行无用存储单元手机清理。这样Servlet对象就销毁了。这四个阶段共同决定了Servlet的生命周期。
Servlet调用过程
1.客户端通过发送请求给Tomcat,Tomcat发送客户端的请求页面给客户端。
2.客户对请求页面进行相关操作将页面提交给Tomcat,Tomcat将其封装成HttpRequest对象,然后对请求进行处理
3.Tomcat截获请求,根据action属性值查询xml文件对应的servlet-name,再根据servlet-name查询到对应java类,如果是第一次,tomcat会将servlet变异成java类文件
4.Tomcat实例化查询到的java类,该类只实例化一次
5.调用java类对象service()方法,不重写情况下根据提交方式决定使用doGet()还是doPost()方法
6.通过对request对象获得客户端传过来的数据,对数据处理后将其封装成request对象返回客户端。
3.Servlet是否是线程安全的
从Servlet的调用过程看,客户端在第一次请求时创建Servlet实例,当又有其他客户端请求时不再实例化该Servlet。因此Jsp/Servlet容器默认采用单实例多线程方式处理多个请求。这种默认以多线程方式执行的设计可大大降低对系统的资源需求,提高系统的并发量和响应时间。
Servlet本身无状态。无状态的Servlet是绝对线程安全的无状态对象设计也是解决线程安全问题的有效手段。
所以,servlet是否线程安全由它的实现决定,若内部属性或方法被多线程改变,它就是线程不安全的,反之,就是线程安全的。
影响Servlet线程安全的因素
多线程下每个线程的局部变脸都有一份自己的copy,这样对局部变量的修改只会影响到自己的copy而不会对别的线程产生影响,是线程安全的
如果是实例变量,由于servlet在Tomcat中是以单例方式存在,所有线程共享实例变量,多个线程对共享资源的访问会造成线程不安全问题。
控制Servlet的线程安全性
1.避免使用实例变量
2.避免使用非线程安全的集合
3.在多个Servlet对某个外部对象的修改务必加锁,互斥访问
4.属性的安全性:ServletContext、HttpSession是贤臣安全的;ServletRequest是非贤臣安全
设计线程安全的Servlet
1.实现SingleThreadModel接口
该接口制定了系统如何处理对同一个Servlet的调用,如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程同时执行,也就不存在线程安全问题。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新请求创建一个单独Servlet实例,这将引起大量系统开销,现在Servlet开发中基本看不到该使用方式,这种方式了解即可,尽量避免使用。
public class XXXXX extends HttpServlet implements SingleThreadModel {
…………
}
2.同步对共享数据的操作
使用synchronized关键字能保证一次只有一个线程可以访问被保护的区段,可以通过同步块操作来保证Servlet的线程安全,如果程序中使用同步来保护要使用的共享数据。也会使系统性能大大下降。因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态,另外为了保证主内存和工作内存中的数据一致性,要频繁地刷新缓存,这也大大影响系统性能,所以在实际开发中也要避免或最小化地使用Servlet的同步代码。
3.避免使用实例变量
贤臣安全问题很大部分是由实例变量造成的,只要在Servlet里面的任何方法都不使用实例变量那么该Servlet就是线程安全的。
Java内存模型中。方法中的临时变量都是在栈上分配空间,每个线程都有自己的线程私有空间,所以他们不会影响线程的安全。
网友评论