Servlet学习——Servlet的线程安全问题

 

目录

1. 什么是线性安全问题

2. JVM中什么数据可能会有线性安全问题

3. Servlet为什么会出现安全问题

4. 线程安全问题举例

5. 解决方案

6. 线程不安全时的作用------统计次数


1. 什么是线性安全问题

同时满足以下两个条件,则会出现线程安全问题。 

  • 存在多线程并发访问
  • 存在可修改的共享数据

当多个线程同时修改同一个共享数据时,后修改的数据会将先修改的数据覆盖,对数据先进行修改的用户读取到的不是自己修改后的数据,这就是线程安全问题

2. JVM中什么数据可能会有线性安全问题

栈内存数据分析 
栈内存是多例的,即 JVM 会为每个线程创建一个栈,所以其中的数据不是共享的。另 外,方法中的局部变量存放在 Stack 的栈帧中,方法执行完毕,栈帧弹栈,局部变量消失。 局部变量是局部的,不是共享的。所以栈内存中的数据不存在线程安全问题。 
堆内存数据分析 
一个 JVM 中只存在一个堆内存,堆内存是共享的。被创建出的对象是存放在堆内存的, 而存放在堆内存中的对象,实际就是对象成员变量的值的集合。即成员变量是存放在堆内存 的。堆内存中的数据是多线程共享的,也就是说,堆内存中的数据是存在线程安全问题的。 
方法区数据分析 
一个 JVM 中只存在一个方法区。静态变量与常量存放在方法区,方法区是多线程共享 的。常量是不能被修改的量,所以常量不存在线程安全问题。静态变量是多线程共享的,所 以静态变量存在线程安全问题

总结:静态变量、成员变量是多线程共享的,可能会出现线性安全问题

3. Servlet为什么会出现安全问题

Servlet 是在单例多线程环境下运行的。其运行可能会出现线程安全问题

 

4. 线程安全问题举例

LoginServlet.java

package com.orbit.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	//定义成员变量,成员变量是可修改的,故线程不安全
	private String username;	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		username = request.getParameter("uname");
		
		PrintWriter out = response.getWriter();
		out.print("username = " + username);	
	}
}

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="login" method="post">
		用户名<input type="text" name="uname"/><br/>
		<input type="submit" value="提交"/>
	</form>
</body>
</html>

使用tomcat的debug模式运行项目:

5. 解决方案

对于 Servlet 的使用,一般是不声明成员变量的。若项目中要求必须要声明成员变量,则只能通过线程同步机制 synchronized 避免线程安全问题.

方案①——将成员变量修改为局部变量

LoginServlet.java

package com.orbit.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		//修改为局部变量
		String username = request.getParameter("uname");
		
		PrintWriter out = response.getWriter();
		out.print("username = " + username);
	}
}

 

使用tomcat的debug模式运行项目:

方案②——将对 username 的操作语句均放入同步语句块 synchronized 中,让所有请求进行串行化排队执行

最好不要使用线程同步机制。因为一旦操作进入串行化的排队状态,将大大降低程序的执行效率。 

LoginServlet.java

package com.orbit.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	//定义成员变量,成员变量是可修改的,故线程不安全
	private String username;	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		//添加同步锁:缺点为一次只能一个用户通过
		synchronized (this) {
			username = request.getParameter("uname");
			
			PrintWriter out = response.getWriter();
			out.print("username = " + username);
		}			
	}
}

结果与方案①一致(图略)

6. 线程不安全时的作用------统计次数

LoginServlet.java

package com.orbit.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/login")
public class LoginServlet extends HttpServlet {
	//定义成员变量,成员变量是可修改的,故线程不安全
	private int count;	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		//该servlet每被访问一次,count就加一
		count++;
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.print("该页面已被访问" + count + "次");
	}
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Servlet 是 JavaEE 规范中的一部分,是处理 Web 请求的组件。Servlet 运行在服务器端,能够接收客户端发来的请求,并给客户端响应结果。下面我们来看看 Servlet 的基本概念和使用方法。 ## 一、Servlet 的基本概念 ### 1.1 Servlet 的生命周期 Servlet 的生命周期包含以下三个阶段: - 初始化阶段(init):当 Servlet 实例化后,Web 容器会调用其 init() 方法进行初始化操作。在此阶段,Servlet 可以执行一些初始化操作,例如读取配置信息、建立数据库连接等。 - 请求处理阶段(service):当客户端发来请求时,Web 容器会创建一个线程调用 Servlet 的 service() 方法处理请求。在此阶段,Servlet 可以获取请求参数、处理请求并生成响应数据。 - 销毁阶段(destroy):当 Web 应用停止或 Servlet 被卸载时,Web 容器会调用 Servlet 的 destroy() 方法进行清理工作。在此阶段,Servlet 可以释放资源、关闭数据库连接等。 ### 1.2 Servlet 的配置 在使用 Servlet 时,需要在 web.xml 文件中进行配置。以下是一个 Servlet 的基本配置: ```xml <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.example.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/myservlet</url-pattern> </servlet-mapping> ``` 其中,servlet-name 表示 Servlet 的名称,servlet-class 表示 Servlet 的类名,url-pattern 表示请求的 URL 匹配规则。 ## 二、Servlet 的使用方法 ### 2.1 编写 Servlet 编写 Servlet 有两种方法:一种是继承 HttpServlet 类,另一种是实现 Servlet 接口。这里以继承 HttpServlet 类为例: ```java public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理 GET 请求 PrintWriter out = resp.getWriter(); out.println("Hello, world!"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理 POST 请求 PrintWriter out = resp.getWriter(); out.println("Hello, world!"); } } ``` 在 Servlet 中,doGet() 方法用于处理 GET 请求,doPost() 方法用于处理 POST 请求。通过调用 HttpServletResponse 对象的 getWriter() 方法可以向客户端返回响应数据。 ### 2.2 部署 Servlet 将编写好的 Servlet 部署到 Web 容器中,有两种方法:一种是将 Servlet 类打成 war 包放到 Web 容器的 webapps 目录下,另一种是通过 Eclipse 等开发工具将 Servlet 部署到 Web 容器中。部署完成后,可以通过访问 Servlet 的 URL 来测试 Servlet 是否正常工作。 ## 三、总结 本文介绍了 Servlet 的基本概念和使用方法。Servlet 是 Java Web 开发中非常重要的组件,掌握 Servlet 的使用方法对于 Java Web 开发人员来说是必不可少的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值