什么是Servlet?
在 JavaWeb 项目中,处理请求和发送响应的过程是由一种叫做 Servlet 的程序来完成的,并且 Servlet 是为了解决实现动态页面而衍生的东西。简单来讲,Servlet其实就是一个遵循Servlet开发的java类,Serlvet是由服务器调用的,运行在服务器端。
Servlet的生命周期
加载并实例化:Servlet容器负责加载和实例化Servelt。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,Servlet通过类加载器来加载Servlet类,加载完成后再new一个Servlet对象来完成实例化。
初始化:在Servlet实例化之后,容器将调用init()方法,并传递实现ServletConfig接口的对象。在init()方法中,Servlet可以部署描述符中读取配置参数,或者执行任何其他一次性活动。在Servlet的整个生命周期类,init()方法只被调用一次。
请求处理:当Servlet初始化后,容器就可以准备处理客户机请求了。当容器收到对这一Servlet的请求,就调用Servlet的service()方法,并把请求和响应对象作为参数传递。当并行的请求到来时,多个service()方法能够同时运行在独立的线程中。通过分析ServletRequest或者HttpServletRequest对象,service()方法处理用户的请求,并调用ServletResponse或者HttpServletResponse对象来响应。
销毁:一旦Servlet容器检测到一个Servlet要被卸载,这可能是因为要回收资源或者因为它正在被关闭,容器会在所有Servlet的service()线程之后,调用Servlet的destroy()方法。然后,Servlet就可以进行无用存储单元收集清理。这样Servlet对象就被销毁了。这四个阶段共同决定了Servlet的生命周期。
Servlet的调用过程
1.客户端通过发送请求给Tomcat,Tomcat发送客户端的请求页面给客户端。
2.用户对请求页面进行相关操作后将页面提交给Tomcat,Tomcat将其封装成一个HttpRequest对象,然后对请求进行处理,。
3.Tomcat截获请求,根据action属性值查询xml文件中对应的servlet-name,再根据servlet-name查询到对应的java类(如果是第一次,Tomcat会将servlet编译成java文件,所以如果有很多servlet的话第一次运行程序会比较慢)。
4.Tomcat实例化查询到的java类,该类只能实例化一次。
5.调用java类对象的service()方法(如果不对service()方法进行重写则根据提交的方式来决定执行doPost()方法还是doGet()方法)。
6.通过request对象取得客户端传过来的数据,对数据进行处理后通过response对象将处理结果写回客户端。
Servlet是单实例多线程环境下运行的,什么时候程序存在线程安全问题?
1.存在多线程并发访问
2.存在可修改的共享数据
当多个线程同时修改同一个共享数据时,后修改的数据会将先修改的数据覆盖,对数据先进行修改的用户读取到的不是自己修改后的数据,这就是线程安全问题
在JVM中
1.局部变量内存空间不共享,一个线程一个栈,局部变量在栈中存储,局部变量不会存在线程安全问题;
2.常量不会被修改,所以常量不会存在线程安全问题;
3.所有线程共享一个堆:new出来的对象在堆内存中存储,对象内部有“实例变量”,所以“实例变量”的内存多线程是共享的,多线程共同访问并且涉及到修改操作的时候就会存在线程安全问题;
4.所有线程共享一个方法区:方法区中有静态变量,静态变量的内存也是共享的,若涉及到修改操作,静态变量也存在线程安全问题。
在数据库中
多个线程共享同一张表,并且同时去修改表中一些记录,那么这些记录就存在线程安全问题。
Servlet怎么解决线程安全问题?
1.把成员变量改为局部变量,局部变量在栈内存中,不存在线程安全问题
2.加线程锁,排队访问
如何合理利用Servlet线程不安全
通过利用线程不安全问题,可以记录servlet被访问的次数代,在servlet中设置一个成员变量,专门用于被公共访问,从而完成记录访问次数