Tomcat与JavaWeb 2.6 避免并发问题、Chapter2小结

本文介绍如何在Servlet中处理并发访问的问题,重点讲解了通过合理设置变量作用域和使用Java同步机制来避免并发问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Internet中,一个Web应用可能被来自四面八方的客户并发访问,而且有可能这些客户并发访问的是Web应用中的同一个Servlet。容器为了保证能同时响应多个客户端的要求访问同一个Servlet的HTTP请求,通常会为每个请求分配一个工作线程,这些工作线程并发执行同一个Servlet对象的service()方法。

当多个线程并发执行同一个Servlet对象的service()方法时,可能会导致并发问题。这是因为不同的客户端同时访问同一个Servlet可能都会对Servlet中的一些值进行读写操作。在解决并发问题时,主要遵循以下原则:

  • 根据实际应用需求,合理决定在Servlet中定义的变量的作用域。变量到底是实例变量,还是局部变量,是由实际应用需求决定的。
  • 对于多个线程同时访问共享数据而导致并发问题的情况,使用Java同步机制对线程进行同步。
  • 不提倡使用被废弃的javax.servlet.SingleThreadModel接口。
1.    合理决定在Servlet中定义的变量的作用域

在java语言中,局部变量和实例变量有着不同的作用域。它们的区别如下:

  • 局部变量在一个方法中定义。每当一个线程执行局部变量所在的方法时,在线程的堆栈中就会创建这个局部变量,当线程执行完该方法后,局部变量就结束生命周期。如果有多个线程同时实行该方法,那么每个线程都拥有自己的局部变量。
  • 实例变量在类中定义。类的每一个实例都拥有自己的实例变量。如果一个实例结束生命周期,那么属于它的实例变量也就结束生命周期。如果有多个线程同时执行一个实例的方法,而这个方法会访问一个实例变量,那么这些线程访问的是同一个实例变量。

例如,如果在web.xml为HandlerServlet类设置了一个URL映射“/handler”,那么该映射其实对应的就是HandlerServlet类的一个实例,所有访问该URL的请求都是由这个实例来处理。如果为HandlerServlet类再设置一个URL映射"/handler2",对应一个新的实例,那么访问这两个URL的请求是分别由HandlerServlet的两个不同的实例来处理的。

2.    使用Java同步机制对多线程同步

AddServlet.java

import javax.servlet.GenericServlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class AddServlet extends GenericServlet {
    private int sum = 100;
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException{
        int increase = Integer.parseInt(servletRequest.getParameter("increase"));
        servletResponse.setContentType("text/html;charset=GB2312");

        PrintWriter out = servletResponse.getWriter();
        out.println("<html><head><title>Adder</title></head>");
        out.println("<body>");
        synchronized (this){           //使用同步代码块
            out.println(sum+"+"+increase+"=");
            try{
                Thread.sleep(3000);               //特意延长响应客户端的时间,便于观察
            }catch (Exception e) {e.printStackTrace();}
            sum += increase;
            out.println(sum);
        }
        out.println("</body></html>");
    }
}

将URL映射配置为“/add”,打开浏览器的两个页面,分别访问该地址,再同时访问该地址,可以观察到该同步代码块中代码的运行情况。Java同步机制确保在任意时刻,只允许有一个工作线程执行AddServlet对象的service()方法中的同步代码块,只有当这个工作线程退出同步代码块时,其他工作线程才允许执行同步代码块,这使得任一时刻不会有两个线程同时操控同一个AddServlet对象的sum实例变量,从而避免并发问题。


Chapter 2 小结

本章介绍了关于Servlet的一些高级技术,主要包括三个方面:

  • 输入和输出。
  • 与其他Web组件的协作。
  • 避免并发问题。

Servlet与客户端交换的正文数据不仅可以为HTML文档,还可以是其他MIME类型的数据。本章介绍了以下几种。

  • "apllication/force-download"类型的数据。当浏览器接收到这种类型的响应正文后,会让用户下载相应正文,用户可以把它保存到客户端的文件系统中。
  • "multipart/form-data"类型的数据。当Servlet接收到这种类型的请求正文时,可以利用Apache开源软件组织提供的fileupload软件包,把客户端的上传文件保存到本地文件系统中。
  • "image/jpeg"类型的图像数据。图像数据的来源可以是服务器端的文件系统中的静态图像文件也可以是由Servlet动态生成的图像。
  • Cookie数据。Cookie的运作机制由HTTP协议规定。Cookie数据不是位于HTTP请求正文和响应正文中,而是位于HTTP请求头和HTTP响应头中。Servlet利用HttpServletResponse接口的addCookie方法,把Cookie数据加入到HTTP响应结果中;Servlet利用HttpServletRequest接口的getCookies()方法,来读取HTTP请求中的所有Cookie数据。
与其他Web组件的协作

包括请求转发、包含、重定向、链接。链接是指在源组件中通过HTML的<a>标记,链接到目标组件。用户线通过浏览器请求访问源组件,然后在源组件返回的HTML页面中选择链接目标组件,请求访问目标组件。源组件和目标组件不共享请求范围内的数据。请求范围内的数据的生命周期与HttpServletRequest的生命周期是相同的。

避免并发问题

  1. 合理决定变量的作用域类型,是实例变量还是局部变量。
  2. 在Servlet的service()方法中,把操纵共享数据的代码块作为同步代码块。Servlet的实例变量是典型的共享数据。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值