servlet是线程安全的吗
来源:互联网 发布:特蕾莎修女知乎 编辑:程序博客网 时间:2024/04/26 07:58
首先什么是线程安全?
引用概念:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
那么我们都知道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>");
}
}
设计线程安全的Servlet
通过上面的分析,我们知道了实例变量不正确的使用是造成Servlet线程不安全的主要原因。下面针对该问题给出了三种解决方案并对方案的选取给出了一些参考性的建议。
1、实现 SingleThreadModel 接口
该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。这种方法只要将前面的Concurrent Test类的类头定义更改为:
1 public class ConcurrentTest extends HttpServlet implements SingleThreadModel {2 ... ... 3 }
javax.servlet.SingleThreadModel API及其翻译
Ensures that servlets handle only one request at a time. This interface has no methods.
确保servlet每次只处理一项请求。接口不含方法。
If a servlet implements this interface, you areguaranteed that no two threads will execute concurrently in the servlet'sservice
method. The servlet container can make this guarantee by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet.
如果servlet实现了该接口,会确保不会有两个线程同时执行servlet的service方法。 servlet容器通过同步化访问servlet的单实例来保证,也可以通过维持servlet的实例池,对于新的请求会分配给一个空闲的servlet。
Note that SingleThreadModel does not solve all thread safety issues. For example, session attributes and static variables can still be accessed by multiple requests on multiple threads at the same time, even when SingleThreadModel servlets are used. It is recommended that a developer take other means to resolve those issues instead of implementing this interface, such as avoiding the usage of an instance variable or synchronizing the block of the code accessing those resources. This interface is deprecated in Servlet API version 2.4.
注意:SingleThreadModel不会解决所有的线程安全隐患。 例如,会话属性和静态变量仍然可以被多线程的多请求同时访问,即便使用了SingleThreadModel servlet。建议开发人员应当采取其他手段来解决这些问题,而不是实现该接口,比如 避免实例变量的使用或者在访问资源时同步代码块。该接口在Servlet API 2.4中将不推荐使用。
2、同步对共享数据的操作
使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。同步后的代码如下:
1 public class ConcurrentTest extends HttpServlet { 2 PrintWriter output; 3 @Override 4 protected void service(HttpServletRequest request, HttpServletResponse response) 5 throws ServletException, IOException { 6 String username; 7 response.setContentType("text/html;charset=gb2312"); 8 username=request.getParameter("username"); 9 synchronized(this){10 output=response.getWriter();11 try {12 //为了突出并发问题,在这设置一个延时13 Thread.sleep(5000);14 output.println("用户名:"+username+"<BR>"); 15 } catch (Exception e) {16 e.printStackTrace();17 }18 }19 }20 }
3、避免使用实例变量
本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。
修正上面的Servlet代码,将实例变量改为局部变量实现同样的功能,代码如下:
1 public class ConcurrentTest extends HttpServlet { 2 @Override 3 protected void service(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 PrintWriter output; 6 String username; 7 response.setContentType("text/html;charset=gb2312"); 8 username=request.getParameter("username"); 9 synchronized(this){10 output=response.getWriter();11 try {12 //为了突出并发问题,在这设置一个延时13 Thread.sleep(5000);14 output.println("用户名:"+username+"<BR>"); 15 } catch (Exception e) {16 e.printStackTrace();17 }18 }19 }20 }
- servlet是线程安全的吗
- servlet默认是线程安全的吗
- Servlet是线程安全的吗?
- Servlet是线程安全的吗?
- servlet是线程安全的吗?
- servlet是线程安全的吗
- servlet是线程安全的吗?
- Servlet是线程安全的吗?
- servlet是线程安全的吗
- Servlet 是线程安全的吗?
- Servlet是线程安全的吗?
- servlet是线程安全的吗?
- Java面试题:Servlet是线程安全的吗?
- Java面试题:Servlet是线程安全的吗?
- 10042---Java面试题:Servlet是线程安全的吗?
- Java面试题:Servlet是线程安全的吗?
- 【java学习准备2】servlet是线程安全的吗?
- servlet是否是线程安全的
- Sping MVC 入门
- Html5 css reset
- Java中的ReentrantLock和synchronized两种锁定机制的对比
- MFC写入文件的尴尬——让ofstream流行起来
- pthread_key_create函数
- servlet是线程安全的吗
- weblogic问题整理
- jquery mobile 入门5 (预加载与缓存页面)
- 软工大作业·源物语(一)
- 【Java开发者自学笔记】从A至Z的路径规划
- NSDictionary转换成JSON字符串
- Android VectorDrawable与SVG
- 使用iphone作为远程仓库的方案
- 使用HttpClient和WebRequest时POST一个对象的写法