背景
Servlet/JSP技术和ASP、PHP等相比,由于其多线程运行而具有很高的执行效率。由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。然而,很多人编写Servlet/JSP程序时并没有注意到多线程安全性的问题,这往往造成编写 的程序在少量用户访问时没有任何问题,而在并发用户上升到一定值时,就会经常出现一些莫明其妙的问题。
线程安全:
一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;通俗的讲就是:在多线程环境下,多个线程对共享变量进行操作后,变量的改变后的值和预期是一样的。若有多个线程同时执行写操作,一般都需要考虑线程安全,否则的话就可能影响线程安全。
Servlet线程安全问题
由于在Web容器中,为了减少每一次请求Servlet的开销的时候,我们通过将Servlet以单利的模式创建,而这个时候由于存在共享变量所以,会出现线程安全的问题。
如果解决线程安全:
1、实现 SingleThreadModel 接口
该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题(注意:SingleThreadModel不会解决所有的线程安全隐患。 例如,会话属性和静态变量仍然可以被多线程的多请求同时访问,即便使用了SingleThreadModel servlet。建议开发人员应当采取其他手段来解决这些问题,而不是实现该接口,比如 避免实例变量的使用或者在访问资源时同步代码块。该接口在Servlet API 2.4中将不推荐使用。)
2、同步对共享数据的操作
使用synchronized关键字,通过对象的锁机制保证同一时间只有一个线程访问变量。当然,这个被用作“锁机制”的变量是多个线程共享的。提供一份变量,让不同的线程排队访问。(以时间换空间)
3、避免使用实例变量
本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。
4、使用ThreadLocal模式
java.lang.ThreadLocal,提供了一种解决多线程并发问题的方案,该类在维护变量时,实际使用了当前线程中的一个叫做ThreadLocalMap的独立副本,每个线程可以独立修改属于自己的副本而不会互相影响,从而隔离了线程和线程,避免了线程访问实例变量发生冲突的问题。ThreadLocal本身并不是一个变量,而是通过操作当前线程的一个内部变量来达到与其他线程隔离的目的。从命名也可以看出操作的对象是线程的一个本地变量。
小结
Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。