1. 线程安全的概念
-
服务器在收到请求之后,会启动一个线程来进行相应的请求处理。
-
默认情况下,服务器为每个
Servlet
只创建一个对象实例。当多个请求访问同一个Servlet
时,会有多个线程访问同一个
Servlet
对象,此时就可能发生线程安全问题。 -
多线程并发逻辑,需要使用
synchronized
对代码加锁处理,但尽量避免使用。
2. 线程安全的案例
-
ThreadServlet
package cn.knightzz.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @author 王天赐 * @title: ThreadServlet * @projectName spring-aop-01 * @description: * @website http://knightzz.cn/ * @github https://github.com/knightzz1998 * @date 2022/1/9 9:56 */ @WebServlet(name="ThreadServlet", urlPatterns = "/thread-servlet") public class ThreadServlet extends HttpServlet { /** * 共享变量 */ private String name; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取参数 name = req.getParameter("name"); // 睡眠5s, 模拟并发 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 获取输出流 PrintWriter writer = resp.getWriter(); writer.println("<h2>" + name + "</h2>"); writer.close(); } }
-
thread.html
使用三个 iframe 是为了模拟并发访问
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Servlet线程安全测试</title> </head> <body> <iframe width="400px" height="100px" src="thread-servlet?name=zhang"></iframe> <br/> <iframe width="400px" height="100px" src="thread-servlet?name=wang"></iframe> <br/> <iframe width="400px" height="100px" src="thread-servlet?name=li"></iframe> </body> </html>
-
下图为执行结果, 如下图所示 , 正常情况下, 每个iframe 的值是不同的, 但是由于并发访问, 此时显示都是
<h2>li</h2>
3. 线程安全的解决方案
-
在共享变量上面加锁
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { synchronized (this) { // 获取参数 name = req.getParameter("name"); // 睡眠5s, 模拟并发 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 获取输出流 PrintWriter writer = resp.getWriter(); writer.write("<h2>" + name + "</h2>"); writer.close(); } }
-
执行结果如下