servlet对象在tomcat服务器是单实例多线程的。
因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。
package com.loaderman.demo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestServlet extends HttpServlet {
int count = 1;
//servlet的多线程并发问题
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("你现在是当前网站的第" + count + "个访客"); //线程1执行完 , 线程2执行
//线程1还没有执行count++
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
解决方法:
1.同步代码块:给使用到共享数据的代码块添加同步锁
注:同步锁必须多个线程唯一
package com.loaderman.demo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestServlet extends HttpServlet {
int count = 1;
//servlet的多线程并发问题
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
synchronized (TestServlet.class) {//锁对象必须唯一。建议使用类对象
response.getWriter().write("你现在是当前网站的第" + count + "个访客"); //线程1执行完 , 线程2执行
//线程1还没有执行count++
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
}
2.同步方法
package com.loaderman.demo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestServlet extends HttpServlet {
int count = 1;
//servlet的多线程并发问题
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
method(response);
}
public synchronized static void method(HttpServletResponse response){
try{
response.getWriter().write("你现在是当前网站的第" + count + "个访客");
}catch (IOExcetion e1) {
e1.printStackTrace();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
开发线程安全的servlet建议:
- 尽量不要使用成员变量,或者静态成员变量
- 必须要使用成员变量时,要么使用了成员变量的代码块加同步锁,加锁的代码块的范围尽量缩小,因为有可能影响程序并发效率。