Servlet--理解

1.servlet运行在服务器上应用程序,它担当一个HTTP客户程序与服务器上数据库之间联系的中间层。擅长处理 流程和业务逻辑。

一.工作原理:服务器只创建每一个Servlet的单一的实例,对于并发请求会有线程安全问题

首次创建servlet时初次调用init()方法,servlet接受到用户的请求时候,为每一个用户分配一个线程,多个并发的请求导致线程同时调用service方法,service方法根据请求类型调用doGET,doPost方法,最后服务器想卸载某个servlet时候首先调用servlet的destroy的方法

二.servlet 的生命周期: servlet被装载--->实例化---->服务---->销毁。

1.Servlet装载,servlet容器加载servlet 调用init()方法进行初始化操作,整个生命周期中,init()方法只能被调用一次

2.service()方法,是servlet的核心servlet接受到用户的请求时候,为每一个用户分配一个线程,多个并发的请求导致线程同时调用service方法,service方法根据请求类型调用doGET,doPost方法

3.destroy() 方法:只执行一次,服务器准备卸载servlet时候才会电调用此方法。

servlet深入理解:

根接口:

 Servlet接口

    |---GenericServlet:不依附特定协议

        |----HttpServlet(主要的实现类):基于HttpServlet:将ServletRequest强制转成HttpRequest

      servlet运行在任何的服务器上,每一个Servlet直接或者间接实现了servlet的接口 ,servlet接口定义五的方法,其中的三个方法 init(),service(),destory()的方法关于生命周期的;

为了servlet设计方便,GenericServlet类实现servlet的接口,不依附任何协议的servlet。而HttpServlet继承GenericServlet类同样实现servlet接口,因此在实际过程 写servlet只需要继承HttpServlet就行

        Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest,ServletResponse强转为HttpRequest和HttpResponse。

三.多个用户同时调用同一个servlet,由于服务器只创建servlet的单一实例,多个请求产生多个线程调用同一个servlet时候出现线程安全问题:

    解决方法:  1.多线程不共享局部变量,尽可能用局部变量

                      2.成员共享变量 加上sychronized进行锁限制

                      3.使用单例模式,对方法进行sychronized,将并发请求转换成串性; 

             1.不能真正共享数据,使用非静态变量存储数据,导致同一个servlet类多个请求申请多个单例对象

              2.使用静态变量存储数据,每一个请求实例化一个单例同样并发访问静态变量,解决共享静态数据问题又引入线程不安全,最好使用sychronized块对共享变量进行上锁

从上理解得出 Servlet服务器开辟多个线程,来处理请求,多个线程访问同一个servlet对象,对于共享的数据不得不 加上sychronized进行同步,或者将全局,静态变量写出局部变量,多线程不共享局部变量

 1)servlet容器为了响应多个用户同时访问一个servlet的HTTP请求时候,servlet容器为每一个请求分配一个工作线程,这些线程同时并发执行servlet的service的方法,发生多线程同时访问同一数据的情况

 1 public class Hello extends HttpServlet{
 2     private String name;
 3     
 4     public void doPost(HttpServletRequest request,HttpServletResponse response)
 5         throws ServletException,java.io.IOException{
 6         
 7         response.setContentType("text/html;charset=UTF-8");
 8         
 9         name = (String)request.getParameter("name");    //接收参数
10         
11         PrintWriter out = response.getWriter();
12         out.println("<html><head><title>test</title></head><body>");
13         out.println("你好"+name);
14         out.println("</body></html>");
15                 
16         out.close();
17     }
18 }

如果多线程并发访问,会访问同一个实例变量,则会共用name,而出现用户得到数据不一致的现象

解决的方式:

1.使用局部变量的方式:

 1     
 2 public class Hello extends HttpServlet{
 3     
 4     public void doPost(HttpServletRequest request,HttpServletResponse response)
 5         throws ServletException,java.io.IOException{
 6         
 7         response.setContentType("text/html;charset=UTF-8");
 8         
 9         String name = (String)request.getParameter("name");    //接收参数
10         
11         ...
12     }
13 }

每当一个线程执行doPost()时,在线程的堆栈中就会创建name这个局部变量,当线程执行完该方法,局部变量就结束生命周期。如果多个线程同时执行该方法,那么每个线程都拥有自己的局部变量

2.java 中使用同步机制:计算共享变量

 1 public class Hello extends HttpServlet{
 2     private String name;
 3     
 4     public void doPost(HttpServletRequest request,HttpServletResponse response)
 5         throws ServletException,java.io.IOException{
 6         
 7         response.setContentType("text/html;charset=UTF-8");
 8         
 9         synchronized(this){
10             name = (String)request.getParameter("name");    //接收参数
11             ...
12         }
13         ...
14     }
15 }

3.实现被废弃的SingleThreadModel接口 :

  对于实现此接口则Servlet容器实现可以采用以下两种方式之一来运行Servlet: 
1)在任意一时刻,只允许有一个工作线程执行Servlet的service()方法。如果有多个用户同时请求访问该Servlet,那么这些客户请求被放入等待队列,容器会依次响应等待队列中的每个客户请求。这种实现方式实际上禁止了多个客户端对同一个Servlet的并发访问。 
2)Servlet容器为每个Servlet创建一个对象池,在这个池中存放了同一个Servlet类的多个实例。如果有多个用户同时请求访问该Servlet,Servlet容器会为每个请求分配一个工作线程,并且从对象池中取出一个空闲的Servlet实例,把它分配给工作线程。每个工作线程执行自己的Servlet实例的service()方法。这种实现方式表面上允许客户端对同一个Servlet并发访问,但实际上不同客户端访问的是同一个Servlet类的不同实例。 
如果实例变量需要共享,用以进行计算,这种方法并不能有效的避免并发问题。

原文地址:https://www.cnblogs.com/woainifanfan/p/6725205.html