开发线程安全的Servlet

来源:互联网 发布:天启挂机 源码 编辑:程序博客网 时间:2024/06/10 15:03

Servlet规范定义,在默认情况下(Servlet不是在分布式的环境部署),Servlet容器对声明的每一个Servlet,只创建一个实例。若有多个客户请求同时访问这个Servlet,Servlet容器将采用多线程,Servlet容器维护了一个线程池来服务请求。线程池实际上是等待执行代码的一组线程,这些线程叫做工作者线程(Worker Thread)。Servlet容器使用一个调度者线程(Dispatcher Thread)来管理工作者线程。当容器接收到一个访问Servlet的请求,调度者线程从从线程池中选取一个工作者线程,将请求传递给该线程,然后由这个线程执行Servlet的service()方法。


当这个线程正在执行的时候,容器收到了另外一个请求,调度者线程将从池中选取另一个线程来服务新的请求。Servlet容器并不关心这第二个请求是访问同一个Servlet还是另一个Servlet。因此,如果容器同事收到访问同一个Servlet的多个请求,那么这个Servlet的service()方法将在多个线程中并发的执行。

由于Servlet容器采用了单实例多线程的方式,最大限度地减少了产生Servlet实例的开销,显著地提升了对请求的响应时间。对于Tomcat,可以在server.xml文件中通过<Connector>元素设置线程池中线程的数目。

1 变量的线程安全
线程对自己栈中的本地变量的改变不会影响其他线程的本地变量的拷贝,因此,在请求处理过程中,user的值将不会被别的线程所改变,在Servlet的开发中,本地变量总是线程安全的。
第二种方法是同步doGet()方法 synchronized 这意味着访问同一个Servlet的请求将排队,一个线程处理完请求后,才能执行另一个线程,这将严重影响性能。
对于Servlet中的类变量(静态变量),因为他们在所有属于该类的实例中共享,所以也不是线程安全的。类变量在Servlet中常被用于存储只读的或常量的数据,例如存储JDBC驱动程序类名,连接URL。
2 属性的线程安全
在Servlet中,可以访问保存在ServletContext,HttpSession和ServletRequest对象中的属性,这三种对象都提供了getAttribute()和setAttribute()方法用于读取和设置属性。
ServletContext对象可以被Web应用程序中所有的Servlet访问,多个线程可以同时在Servlet上下文中设置或者读取属性,这将导致存储数据的不一致。
HttpSession对象在用户会话期间存活,他不像ServletContext对象,可以在Web应用程序的所有线程中被访问,HttpSession对象只能在处理属于同一个Session的请求的线程中被访问。
Servlet容器对它所接收到的每一个请求,都创建一个新的ServletRequest对象,所以ServletRequest对象只在一个线程中被访问。因为只有一个线程服务请求,所以请求对象的属性访问是线程安全的。要注意的是,ServletRequest对象是作为参数传进Servlet的service()方法中的,,在service()方法的范围内,该请求对象是有效的不要试图在service()方法结束后,仍然保存请求对象的引用,如果那样的话,请求对象的行为将不可预料。
提示:在多线程的环境中使用集合类对象时,应该使用同步的集合类,例如Vector代替ArrayList,用HashTable代替HashMap。

在开发Servlet时,要注意线程安全的问题:一个是变量的线程安全,本地变量总是线程安全的,实例变量和类变量不是线程安全的;另一个是属性的线程安全,请求对象的属性访问是线程安全的,而Session对象和上下文对象的属性访问不是线程安全的。

建议:
▶尽可能地在Servlet中使用本地变量
▶应该使用只读的实例变量和静态变量
▶不要在Servlet中创建自己的线程
▶修改共享对象时,一定要使用同步,尽可能地缩小同步代码的范围,不要直接在service()方法或doXXX()方法上进行同步,以免影响性能。
▶如果在多个不同的Servlet中,要对外部对象(例如,文件)进行修改操作,一定要加锁,做到互斥的访问。


























1 0