`
javaG
  • 浏览: 553059 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Servlet的线程安全问题

    博客分类:
  • java
阅读更多

这个问题网上一直没有搜到很详细的解释,也可能是高人的解释不符合我的理解方式。所以自己到网上搜集了写资料再加自己的想法,随便写了点东西发到论坛上,希望大家给予修正意见,看我是否理解对了。

一般servlet在jvm中只有个对象,当多个请求来请求一个jsp页面的时候,实际上都是调用这个jsp编译好的servlet类doPost或者doGet方法。

现在我就模拟一个servlet的调用过程

new Runnalbe{
	public run(){
		Request requset = new Request();
		Resposne response = new Response();
               //servlet对象只有一个,是容器自动生成的,这里模拟一个servlet的调用过程。
		servlet.doPost	(request,response);
	}
}


当有多个请求过来的时候,相当于多个线程来执行这段代码。上面那个servlet的实现类HelloServlet:

public class HelloServlet extends HttpServlet {
	private int j =0;
    public void doPost(HttpServletRequest request, HttpServletResponse response){
    	int i=0;
    	i++;
    	j++;
		//这里的i和j那个是线程安全的那个不是呢,后面我们将从线程的堆栈,和jvm的堆的概念来解释这个问题
//request 和 response  对象是不是线程安全的
   }
}


JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈(这里的堆栈不是指堆).也就是说,对于一个Java程序来说,它的运行
就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个
Java方法,JVM就会在线程的Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的。

这里还要补充一下堆的概念:

堆(heap)是放实例和数组的,JAVA里面没有全局变量这个概念,所有变量都是以类的属性或者参数等形式存在的。GC是自动回收.但是数组和类的引用是放在堆栈中。

学过汇编的可能都知道,数据是是存储在栈内的,执行的代码逻辑是可以共用的。当多个线程来访问同一个方法的时候,共享同一段代码逻辑,但是方法对应的数据是存储在各自的堆栈(stack)中,如局部变量和参数还有对象的引用(局部变量和参数也可能是对象的引用)。所以多线程并发的情况下,出现不同步的现象主要是因为各自堆栈存放的某些数据是共享的,说白了就是同一个数据的引用(不是copy)被存放在不同的堆栈中。 例如类X的对象A被多个线程访问,他的引用被保存在多个线程的堆栈中,当多个线程访问A对象的某个属性b的时候如果不加锁就会出现不同步的现象。所以为了避免这种情况发生一是加锁,二就是为每个线程都生成一个类X的对象,这样每个线程的堆栈中存放的类X引用所对应的堆中的对象都不一样,当然就不存在共享的问题。

 

现在我们回到开始那个例子,可以很好的分析出参数request,respone,i是线程安全的,而j是线程不安全的;

为什么?request,respone是线程安全的是因为每个线程对应的request,respone对象都是不一样的,不存在共享问题。i是线程安全的是应为i是局部变量,每个线程的堆栈中存放的值也是各自独立的。

j线程不安全是应为它是类HelloServlet的属性,找了很多资料都不能说清楚它到底放在那里,从实际效果来看,它是不安全的,所以应该是放在和类对象一起放在堆里面的,堆里面估计是复制了一份过来,因为HelloServlet对多个线程而言只有

一个实例,所以存在共享问题。

 

分享到:
评论
24 楼 PetriNet 2009-07-24  
很奇怪,这么多人讨论servlet,就没人看过servlet spec?
23 楼 martinfans 2009-07-09  
kaneg 写道
其实说简单一点就是:
servlet是单实例多线程运行方式,所以对象变量线程不安全,局部变量线程安全


同意! local variable不存在race condition, 但是instance variable存在race condition.
请诸位看这里,
http://book.javanb.com/java-concurrency-in-Practice/ch02lev1sec2.html

The definition of race condition,
The possibility of incorrect results in the presence of unlucky timing is so important in concurrent programming that it has a name: a race condition.
22 楼 kuaileqingfeng 2009-06-17  
看了上面大家的讨论,我的理解是:属于某个线程的数据不会出现同步的问题,例如方法中的局部变量,而不属于线程的数据,例如类中的成员变量,由于他是对该类的对象共享的,如果多个线程拥有一个对象的同一个引用,就会出现同步的问题。
21 楼 wangacidlemon 2009-06-01  
Java的内存模型JMM(Java Memory Model)JMM主要是为了规定了线程和内存之间的一些关系。根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有实例变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存由缓存和堆栈两部分组成,缓存中保存的是主存中变量的拷贝,缓存可能并不总和主存同步,也就是缓存中变量的修改可能没有立刻写到主存中;堆栈中保存的是线程的局部变量,线程之间无法相互直接访问堆栈中的变量。
20 楼 ghostsun 2009-05-26  
楼主分析的很好,最近我也在研究servlet为什么不是多线程的,开会了,回来再说,呵呵。。。
servlet是单例的还是很有道理的!
19 楼 kimmking 2009-03-09  
wujie2008 写道
moshalanye 写道
    一般我们新建对象,jvm 才会为我们新建存放对象的堆空间,里面有属于对象的方法体和属性,而对对象的引用和基础数据类型是存放在栈中的,而cpu运作时,进行运算而产生的中间结果会放在cpu的寄存器中。
    所以基础数据声明后,不管怎么赋值,都不会有线程问题(int i=1;int j=i)两个int 类型的数据根本就是2个数据体。
    局部变量本来就是在某个线程的生命周期内声明的变量,如果不用特别的方法保存它,其他线程根本没办法引用到它,所以也就没有线程同步的问题存在
    而引用同一个对象,修改得是同一个堆中的数据,线程同步腐蚀就出现了,如果没有状态数据,被使用对象也就没有被线程修改得可能,也就没有线程安全问题


说的不错,很认同,如《 进行运算而产生的中间结果会放在cpu的寄存器中》 这句话,比如说j=5;j在第一个线程自增后的结果6保存在CPU的临时寄存器中,假如寄存器自增的值6没有及时回写到内存中去,而同时又有另一个线程执行了此代码,则刚才有第一个线程自增的结果将被第二个线程清洗掉,然后后一个线程在j原先的基础上5上自增1为6,回写到内存。退出代码,假如此时第一个线程被唤醒,再次见寄存器中的值写入到内存中,还是6。经过了两次自增并应该是7,但内存中的值是6,就导致了线程不同步,主要是由于自增运算造成的,而基础数据的任何的赋值操作是不会导致线程安全问题。

如果一个对象下一刻的值 需要依赖原来的值,
就会在多线程下有同步的问题
18 楼 wujie2008 2009-03-09  
moshalanye 写道
    一般我们新建对象,jvm 才会为我们新建存放对象的堆空间,里面有属于对象的方法体和属性,而对对象的引用和基础数据类型是存放在栈中的,而cpu运作时,进行运算而产生的中间结果会放在cpu的寄存器中。
    所以基础数据声明后,不管怎么赋值,都不会有线程问题(int i=1;int j=i)两个int 类型的数据根本就是2个数据体。
    局部变量本来就是在某个线程的生命周期内声明的变量,如果不用特别的方法保存它,其他线程根本没办法引用到它,所以也就没有线程同步的问题存在
    而引用同一个对象,修改得是同一个堆中的数据,线程同步腐蚀就出现了,如果没有状态数据,被使用对象也就没有被线程修改得可能,也就没有线程安全问题


说的不错,很认同,如《 进行运算而产生的中间结果会放在cpu的寄存器中》 这句话,比如说j=5;j在第一个线程自增后的结果6保存在CPU的临时寄存器中,假如寄存器自增的值6没有及时回写到内存中去,而同时又有另一个线程执行了此代码,则刚才有第一个线程自增的结果将被第二个线程清洗掉,然后后一个线程在j原先的基础上5上自增1为6,回写到内存。退出代码,假如此时第一个线程被唤醒,再次见寄存器中的值写入到内存中,还是6。经过了两次自增并应该是7,但内存中的值是6,就导致了线程不同步,主要是由于自增运算造成的,而基础数据的任何的赋值操作是不会导致线程安全问题。
17 楼 ysen 2009-03-06  
Element&lina 写道
sdh5724 写道
一个servlet的非线性安全说的这么复杂。哥们, 更简单的理解问题哦。
servlet是否线程安全取决容器的实现, 一般来说, servlet不是线程安全的, 你自要实现一个也无所谓。

共享会不会造成线程安全也是不对的.

确切的说, 如果一个对象的实例, 在多线程环境下, 如果API CALL会有状态的, 那么他一般就不是线程安全的。

简单明了,同意

16 楼 Element&lina 2009-01-21  
sdh5724 写道
一个servlet的非线性安全说的这么复杂。哥们, 更简单的理解问题哦。
servlet是否线程安全取决容器的实现, 一般来说, servlet不是线程安全的, 你自要实现一个也无所谓。

共享会不会造成线程安全也是不对的.

确切的说, 如果一个对象的实例, 在多线程环境下, 如果API CALL会有状态的, 那么他一般就不是线程安全的。

简单明了,同意
15 楼 unsid 2009-01-21  
kjj 写道
我汗,不知道楼主到底像指导什么,servlet 本来就不是线程安全的,要安全实现一个好像叫做siglelon 啥的接口就线程安全了,不过性能也下降了,自己衡量,请参看sun的jee 文档,有例子说的非常清楚


我先前刚好以为的和你说的相反,我以为容器为每个请求都创建一个新的servlet实例,这种情况下是线程安全的,如果实现singleModel什么的接口,系统将只创建一个servlet实例,分配给所有线程,这样就不安全了,看这个名字singleXX就不像是什么安全的东西,在servlet2.0版本中就不建议使用了,不过是为了节省内存开销。。

现在看大家都这么说,我估计是不是我的想法错了。。
14 楼 javatestoracle 2009-01-20  
servlet 是单实例的,多个线程可能会同时运行,当然这个实例是放在堆里面,由于堆是给多个线程共享的,每个线程的栈,都会referce这个实例,如果这个实例有实例变量,那不好意思,我们都可以访问,所以会早晨不安全的情况,请参看深入java虚拟机第二版 57页
13 楼 lkjust08 2009-01-05  
kaneg 写道
其实说简单一点就是:
servlet是单实例多线程运行方式,所以对象变量线程不安全,局部变量线程安全


说的很对,受教了。
12 楼 taga 2009-01-03  
"servlet 本来就不是线程安全的"
没错,同一个servlet对于不同用户的请求使用不同的线程处理
11 楼 kjj 2009-01-03  
我汗,不知道楼主到底像指导什么,servlet 本来就不是线程安全的,要安全实现一个好像叫做siglelon 啥的接口就线程安全了,不过性能也下降了,自己衡量,请参看sun的jee 文档,有例子说的非常清楚
10 楼 sdh5724 2009-01-01  
一个servlet的非线性安全说的这么复杂。哥们, 更简单的理解问题哦。
servlet是否线程安全取决容器的实现, 一般来说, servlet不是线程安全的, 你自要实现一个也无所谓。

共享会不会造成线程安全也是不对的.

确切的说, 如果一个对象的实例, 在多线程环境下, 如果API CALL会有状态的, 那么他一般就不是线程安全的。
9 楼 frenchmay 2009-01-01  
1)所以多线程并发的情况下,出现不同步的现象主要是因为各自堆栈存放的某些数据是共享的,说白了就是同一个数据的引用(不是copy)被存放在不同的堆栈中。

准确点说是堆数据共享

2)例如类X的对象A被多个线程访问,他的引用被保存在多个线程的堆栈中,

一个java虚拟机中只有一个堆被多个线程共享
8 楼 allenofchina 2008-08-29  
楼上写得不错
我也没有验证过
7 楼 neora 2008-08-29  
allenofchina 写道

而servlet是否是单例取决于容器的实现
一般来说不是单例的


不对吧,一般来说Servlet都是单例的。

public class TestServletSingle extends HttpServlet {
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		response.getOutputStream().println(this.hashCode());
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}


你在5秒内打开N个浏览器窗口试试,返回的都是相同的Hashcode。只有在context reload以后,Hashcode才发生变化。因为原来的servlet object销毁了,容器又新建了一个。

然而,Servlet的单例不由class来决定,而是由您在web.xml里配置的servlet-name来决定。也就是说每一个servlet-name只有1个单例。当你有3个不同的serlvet-name指定了相同的1个servlet-class时,容器会产生3个不同的servlet object。只有3个。
6 楼 allenofchina 2008-08-28  
你例子举的不好,概念也不完全对
其实就是一个线程安全的问题
和servlet没有关系
你这个j是否线程安全取决于你的servlet是否是同一个对象
而servlet是否是单例取决于容器的实现
一般来说不是单例的
5 楼 wangzhongjie 2008-08-28  
地址引用,也可以归类到堆栈这些东西里面把

相关推荐

    servlet线程安全问题

    Servlet 线程安全问题 Servlet 线程安全问题是指在使用 Servlet 编程时,如果不注意多线程安全性问题,可能会导致难以发现的错误。Servlet/JSP 技术由于其多线程运行而具有很高的执行效率,但这也意味着需要非常...

    Servlet线程安全问题.docx

    Servlet线程安全问题详解 在Java Web开发中,Servlet是一个重要的组件,用于处理HTTP请求。然而,Servlet在多线程环境下的线程安全问题是一个不容忽视的话题。在Servlet的生命周期中,Tomcat容器会根据需求实例化...

    深入研究Servlet线程安全性问题.pdf

    #### 三、Servlet线程安全问题分析 线程安全问题是由于Servlet中实例变量的使用不当导致的。在多线程环境中,如果多个线程同时修改相同的实例变量,则可能导致数据不一致性问题。例如,在上面给出的`ConcurrentTest...

    Servlet线程安全的解决方法

    #### 一、理解Servlet线程安全问题 Servlet是Java平台上的一个重要的Web组件模型,用于处理HTTP请求并返回响应。每当客户端向服务器发送请求时,服务器会创建一个线程来处理这个请求。由于Servlet是单例模式实现的...

    深入研究Servlet线程安全性问题

    ### Servlet线程安全问题实例分析 以一个简单的Servlet为例,该Servlet接收用户请求,读取参数,并输出结果: ```java public class Test extends HttpServlet { private static final long serialVersionUID = 1...

    servlet与Struts action线程安全问题分析

    Servlet和Struts Action是两种常见的Java Web开发组件,它们在多线程环境下运行时可能存在线程安全问题。线程安全是指在多线程环境中,一个类或者方法能够正确处理多个线程的并发访问,保证数据的一致性和完整性。 ...

    servlet与Struts action线程安全问题分析(pdf)

    #### 三、Servlet线程安全问题 线程安全问题主要发生在Servlet中使用实例变量时。当多个线程并发访问同一个Servlet实例时,如果这些线程试图同时修改实例变量,就可能发生数据不一致的情况。以下通过一个具体的示例...

    Servlet网上售票问题引发线程安全问题的思考

    在讨论了Servlet线程安全问题的代码实现后,让我们来总结一下Servlet线程安全问题的几个要点: 1. 线程安全问题产生的原因:在多用户环境下,多个线程同时访问和修改共享资源(如Servlet中的`ticket`变量)时,如果...

    Servlet是线程不安全的1

    Servlet是一种线程不安全的组件,它的线程不安全性体现在多线程环境下共享一个实例变量,导致线程安全问题。下面我们将从Servlet的工作原理说起,详细解释Servlet接收和响应客户请求的过程,并探讨Servlet线程不安全...

    Servlet线程同步计数器实验

    Servlet线程同步计数器实验是Java Web开发中一个重要的实践课题,主要涉及到Servlet容器如何处理并发请求以及如何确保在多线程环境下的数据一致性。在这个实验中,我们将深入理解Servlet生命周期、线程模型以及线程...

    servlet多线程

    4. **避免在Servlet中创建额外线程**:由于Servlet本身已具备多线程特性,额外创建线程可能会引入复杂的线程安全问题,除非有特殊需求。 5. **对外部资源的线程安全操作**:在多个Servlet中对同一外部资源(如文件...

    1工作临时-servlet 多线程问题

    在这个场景下,可能的问题可能是由于Servlet在处理高并发请求时,由于多线程并发访问共享资源导致的线程安全问题。 在Java Web环境中,Servlet容器(如Tomcat、Jetty等)会为每个HTTP请求创建一个新的线程来处理,...

    韩顺平Servlet学习源代码

    5. **Servlet线程安全问题**: Servlet容器通常会为每个请求创建一个新的线程,因此,Servlet实例默认是线程安全的。但是,如果在Servlet中定义了成员变量,需要考虑多线程环境下可能产生的并发问题。 6. **...

Global site tag (gtag.js) - Google Analytics