servlet是线程安全的吗?NO
这个问题,在网上没有看到一个确切的答案,所以我们来分析一下:
首先什么是线程安全?
引用概念:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
那么我们都知道servlet是多线程的,同时一个servlet实现类只会有一个实例对象,也就是它是Singleton的,所以多个线程是可能会访问同一个servlet实例对象的。
每个线程都会为数据实例对象开辟单独的引用,那么servlet会是线程安全的吗?
要判断是否是线程安全,我们需要知道线程安全问题是由什么引起的。
搜索得到答案:线程安全问题都是由全局变量及静态变量引起的。
看到这个答案,突然想起很多年前调查过的一个bug, 那时我们系统中遗留的代码中写了很多全局变量,有一次发布后,客户反馈,当有多人同时进行某个操作时,我们的数据出了问题,那时我们调查后的结果就是:多人同步操作时,有些全局变量的值不对了,之后我们专门设一个人花了很多工夫来将所有全局变量都改成了局部变量了,并且项目要求以后不允许用全局变量。原来那时侯我就已经碰到过线程不安全的情况了啊,不过处理方式或者不用全局,或者加入同步,若加入同步同时也要考虑一下对程序效率会不会产生影响。
由此可知,servlet是否线程安全是由它的实现来决定的,如果它内部的属性或方法会被多个线程改变,它就是线程不安全的,反之,就是线程安全的。
在网上找到一个例子,如下:
public class TestServlet extends HttpServlet { private int count = 0; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("<HTML><BODY>"); response.getWriter().println(this + " ==> "); response.getWriter().println(Thread.currentThread() + ": <br>"); for(int i=0;i<5;i++){ response.getWriter().println("count = " + count + "<BR>"); try { Thread.sleep(1000); count++; } catch (Exception e) { e.printStackTrace(); } } response.getWriter().println("</BODY></HTML>"); } }
当同时打开多个浏览器,输入http://localhost:8080/ServletTest/TestServlet时,他们显示的结果不同,这就说明了对于属性count来说,它是线程不安全的,
为了解决这个问题,将代码重构,如下:
public class TestServlet extends HttpServlet { private int count = 0; private String synchronizeStr = ""; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("<HTML><BODY>"); response.getWriter().println(this + " ==> "); response.getWriter().println(Thread.currentThread() + ": <br>"); synchronized (synchronizeStr){ for(int i=0;i<5;i++){ response.getWriter().println("count = " + count + "<BR>"); try { Thread.sleep(1000); count++; } catch (Exception e) { e.printStackTrace(); } } } response.getWriter().println("</BODY></HTML>"); } }