首先明确,有状态和无状态是Servlet.
这个就是无状态的Servlet。没有实例变量,或者实例变量中,无可变的实例(常见的service中的dao对象),
@WebServlet("/stateless")//这里没用持久层框架,使用的是servlet注解。
public class Stateless extends HttpServlet {//i仅仅存在线程栈中,也就是当前线程可用。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int i = 0;
i++;
}
}
///
这个就是有状态的Servlet
@WebServlet("/stateless")
public class Stateless extends HttpServlet {
private int i = 0;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//所有线程都可以访问i变量,并非线程安全的
i++;
}
}
i++实际上包含三个操作,第一个是读取i的值,第二是将i的值加1,第三是把累加后的值赋值给i。
如果没来一个请求,i的值就加1,实际上是不准确的,因为,可能多个线程之间,同时进入这个方法,当a线程刚刚读取i的值后,b线程就完成了累加操作,就会有误差。
///
解决上面多线程环境下,数据安全问题 可以使用
@WebServlet("/stateless")
public class Stateless extends HttpServlet {
public final AtomicLong num =new AtomicLong(0);//AtomicLong为原子操作,AtomicLong书上说后面章节解析
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
num.getAndIncrement();
}
}
即上面的三个操作,为一个原子操作,可以解决并发问题。
///
也可以使用同步代码块,关键字synchronize,即该方法只有一个线程进入,其他线程等待,也是也个原子操作,该代码块将做一个不可分割的单元执行。
重入
某个固定线程可以获取它自己持有的synchronize代码块锁,这个现象就是重入。