`
javaG
  • 浏览: 550142 次
  • 性别: 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线程安全问题.docx

    Servlet是线程不安全的。Servlet体系是建立在java多线程的基础之上的,它的生命周期是由Tomcat 来维护的。当客户端第一次请求Servlet的时候,tomcat会根据web.xml配置文件实例化servlet, 当又有一个客户端访问该...

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

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

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

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

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

    比较深刻地论述了Servlet线程安全性问题

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

    servlet与Struts action线程安全问题分析 <br>===================================================== Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要...

    Servlet线程安全的解决方法

    当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致,所以就很容易造成一系列的一些安全性问题。

    Servlet是线程不安全的1

    先从Servlet的工作原理说起:首先简单解释一下Servlet接收和响应客户请求的过程,首先客户发送一个请求,Servlet是调用service()方法对请求

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

    主要是关于Servlet模拟网上售票问题,引发的线程安全问题的思考,感兴趣的小伙伴们可以参考一下

    Java学习笔记-个人整理的

    {8.1}线程的常用属性与方法}{121}{section.8.1} {8.2}后台线程}{123}{section.8.2} {8.3}创建线程的两种方法}{123}{section.8.3} {8.4}Runnable}{123}{section.8.4} {8.5}Sleep阻塞与打断唤醒}{124}{section....

    JSP和Servlet面试题

    6.如何处理Servlet的线程不安全问题 1.最简单的就是不使用字段变量, 2.使用final修饰变量, 3.线程安全就是多线程操作同一个对象不会有问题,线程同步一般来保护线程安全, 所以可以在Servlet的线程里面加上同步...

    Java web中servlet学习笔记 核心

    Java web中servlet学习笔记 核心。servlet执行过程、servlet生命周期、继承类、创建servlet、servlet线程安全、配置信息

    servlet 模拟

    想简单模拟下servlet,看下什么是线程安全,现在发现自己写的程序问题了,果然线程不安全。

    培训资料_servlet

    1、Servlet基本概念 2、Servlet基本运用、配置 3、Servlet生命周期 4、Servlet线程安全 5、Model2与MVC设计模式 6、过滤器 7、分页 8、上传组件SmartUpload 9、监听器 10、配置Tomcat连接池 11、实用技术

    java-servlet-api.doc

    在多线程的环境下,Servlet必须能处理许多同时发生的请求。例外的情况是这个Servlet执行了SingleThreadModel接口,如果是那样的话,Servlet只能同时处理一个请求。 Servlet依照Servlet引擎的映射来响应客户端的请求...

Global site tag (gtag.js) - Google Analytics