目录
一、Servlet的生命周期
二、Servlet的生命周期演示图
三、线程安全问题
四、解决线程安全问题
面试题
一、Servlet的生命周期
1.类加载和实例化
Servlet容器(如Tomcat)负责加载和实例化Servlet。当容器是首次启动或首次请求某个Servlet时,容器就会读取web.xml或@WebServlet中的配置信息,对指定的Servlet进行加载。加载成功后,容器会通过反射对Servlet进行实例化,这里反射的本质就是调用Servlet的无参构造方法。容器会调用ServletContext的getServlet方法来获取Servlet实例。
2.初始化
Servlet实例化之后,Servlet容器调用Servlet的init()方法,初始化的目的是为了让Servlet在处理请求之前完成一些初始化的工作,比如建立与数据库的连接,获取配置信息等。在servlet的整个生命周期内,该方法只会被执行这一次。
3.就绪状态
当Servlet完成初始化后,容器就会将它放入就绪状态,表示它已经准备好处理客户端请求了。
3.客户端发送一个请求,Servlet容器解析并处理请求
当客户端发起请求时,Servlet容器会为每个请求创建一个新的线程,并且调用Servlet的service()方法处理请求。当Servlet容器接收到来自客户端的请求时,容器会针对该请求分别创建一个ServletRequest对象和ServletResponse对象,并将他们以参数的形式传入service()方法内,后续通过调用该方法处理请求。在service()方法中,Servlet通过ServletRequest对象获取客户端的相关信息和请求信息。请求处理完后,又通过ServletResponse对象将响应信息进行包装返回给客户端。当Servlet容器将响应信息返回给客户端后,这两个对象就会被销毁,即这两个对象的生命周期是单次请求内。在Servlet的整个生命周期内,对于Servlet的每一次请求,Servlet容器都会调用一次service()方法,并创建新的ServletRequest和ServletResponse对象。
4.销毁阶段
当Servlet容器关闭或重启时,servlet的生命周期结束。容器就会调用destory()方法,释放该实例Servlet使用的资源,如关闭数据库连接、保存会话数据等。随后该实例会被Java的垃圾回收器回收。在Servlet的整个生命周期内,该方法只被调用一次。
二、Servlet的生命周期演示图
三、线程安全问题
Servlet是线程不安全的,通过Servlet的生命周期发现,它是个单例模式。正是因为单例模式才更应该要考虑线程安全问题。当每个用户请求时,都会为他们创建一个新的线程。即单实例多线程。优点是,缺点是存在线程安全问题。比如Servlet代码逻辑中有去全局变量。多线程下所有线程共享实例变量。出现临界值,就会引发线程安全问题。
四、解决线程安全问题
- 不使用实例(全局)变量。事实上,线程安全问题大多是由实例(全局)变量造成的。建议使用。
- 使用Synchronized加锁,保证一次只有一个线程可以访问被保护的区段,加锁实际上也是将多线程并发执行变成串行执行了。这样当大量用户访问同一资源时,用户只能排队一个一个访问,就会出现阻塞状态,造成用户的不好体验,不建议使用。