java servlet拾遗(3)-servlet 线程安全有关问题
java servlet拾遗(3)-servlet 线程安全问题
Servlet容器启动时,会对每一个Servlet对象实例化一次,而且是仅仅一次,在运行的时候,不管多少个请求都是同时执行这一个Servlet对象实例。也就是说Servlet对象是单实例多线程,这个时候,就需要注意到并发安全问题。
一、为什么不安全
先看两个定义:
- 实例变量:实例变量在类中定义。类的每一个实例都拥有自己的实例变量,如果多个线程同时访问该实例的方法,而该方法又使用到实例变量,那么这些线程同时访问的是同一个实例变量,会共享该实例变量。
- 局部变量:局部变量在方法中定义。每当一个线程访问局部变量所在的方法时,在线程的堆栈中就会创建这个局部变量,线程执行完这个方法时,该局部变量就被销毁。所有多个线程同时访问该方法时,每个线程都有自己的局部变量,不会共享。
例如:
public class MyServlet extends HttpServlet{ private static final long serialVersionUID = 1L; private String userName1 = null;//实例变量 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ userName1 = req.getParameter("userName1"); String userName2 = req.getParameter("userName2");//局部变量 //TODO 其他处理 } }
userName1则是共享变量,多个线程会同时访问该变量,是线程不安全的。
userName2是局部变量,不管多少个线程同时访问,都是线程安全的。
二、怎么解决
- 如果不涉及到全局共享变量,全部放到局部变量,最好的做法
- 如果使用到全局共享的场景,可以使用加锁的方式,比如
public class MyServlet extends HttpServlet{ private static final long serialVersionUID = 1L; private String userName1 = null;//实例变量 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ synchronized (userName1) { userName1 = req.getParameter("userName1"); //TODO } String userName2 = req.getParameter("userName2");//局部变量 //TODO 其他处理 } }
或者使用线程安全的StringBuffer去代替String,
或者使用线程安全的容器比如: java.util.concurrent.ConcurrentHashMap 等等
最好使用乐观锁的形式去加锁