V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
HelloWorld11
V2EX  ›  Java

Java 多线程问题

  •  2
     
  •   HelloWorld11 · 2015-04-28 00:02:10 +08:00 · 4210 次点击
    这是一个创建于 3533 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位,晚上好!最近在学 Java Servlet,遇到一个问题,请大家帮忙看看:

    我们都知道,一般情况下,Servlet是单实例 多线程的。

    假设现在有一个实例,A、B两个用户同时用到了这个实例,假设A、B同时访问 service()方法。那么应该是有2个线程。 我的问题是,因为 service()方法的入口(跟C++函数的入口应该一样吧)只有一个,A、B同时(就是同时,不是同时段哈~)调用service()方法的话,不会冲突吗?

    对了,现在服务器都是多核,支持多线程的,A、B两个线程恰好同时刻运行应该是可以的吧。 现在不管有没有什么共享变量,线程安全的问题。 我就是想知道,2个线程同时访问一个方法,会发生什么???或者,在JVM的调度中,是不是根本不会让这两个线程同时运行,即使服务器支持多线程。

    请大家帮忙,让小白理解哈~~~

    30 条回复    2015-05-12 11:03:25 +08:00
    wickila
        1
    wickila  
       2015-04-28 00:08:22 +08:00 via Android   ❤️ 1
    两个线程访问同时调用一个方法会造成你方法里面的那些变量值都不是你预期的那样,可能A线程在执行的时候里面某个变量被B线程给改变了,所以一般都需要加线程锁
    ryd994
        2
    ryd994  
       2015-04-28 00:59:29 +08:00 via Android   ❤️ 1
    这叫可重入性,reentrancy
    似乎Java对此有专门的锁
    ryd994
        3
    ryd994  
       2015-04-28 01:04:18 +08:00 via Android   ❤️ 1
    我又查了一下,Java所有的方法都是可重入的
    重入锁是为了防止重入的时候自己把自己死锁而设置的。
    ryd994
        4
    ryd994  
       2015-04-28 01:04:47 +08:00 via Android   ❤️ 1
    @wickila Java作为动态语言还怕重入?
    phx13ye
        5
    phx13ye  
       2015-04-28 01:07:48 +08:00 via Android   ❤️ 1
    都进自己线程栈顶并执行
    miaoever
        6
    miaoever  
       2015-04-28 01:14:31 +08:00   ❤️ 1
    线程会有自己的stack,所以各自执行 service 没有任何问题。当然读写共享变量就会产生各种 race condition 的问题。
    qiyi
        7
    qiyi  
       2015-04-28 01:42:36 +08:00 via Android   ❤️ 1
    摘自jvm规范(2.6)
    A frame is used to store data and partial results, as well as to perform dynamiclinking, return values for methods, and dispatch exceptions.A new frame is created each time a method is invoked. A frame is destroyed whenits method invocation completes, whether that completion is normal or abrupt (itthrows an uncaught exception). Frames are allocated from the Java Virtual Machinestack (§2.5.2) of the thread creating the frame. Each frame has its own array oflocal variables (§2.6.1), its own operand stack (§2.6.2), and a reference to the run-time constant pool (§2.5.5) of the class of the current method.
    A frame may be extended with additional implementation-specific information, such asdebugging information.The sizes of the local variable array and the operand stack are determined atcompile-time and are supplied along with the code for the method associated withthe frame (§4.7.3). Thus the size of the frame data structure depends only on theimplementation of the Java Virtual Machine, and the memory for these structurescan be allocated simultaneously on method invocation
    helloworld00
        8
    helloworld00  
       2015-04-28 04:02:22 +08:00   ❤️ 2
    lz 我们是同一个class new出来的object吗?
    Andiry
        9
    Andiry  
       2015-04-28 04:11:53 +08:00   ❤️ 3
    同步保护的不是方法,而是数据
    zts1993
        10
    zts1993  
       2015-04-28 07:36:35 +08:00 via Android   ❤️ 1
    用局部变量不就没有问题了。加群并不是一个好方案
    kifile
        11
    kifile  
       2015-04-28 07:43:41 +08:00   ❤️ 1
    对于局部变量没有影响,只不过对于成员变量就可能导致发生值改变,得到的数据与预期不符,应该要加锁
    flaty
        12
    flaty  
       2015-04-28 08:38:15 +08:00   ❤️ 1
    为什么说到重入了..~~
    多线程,单实例,每个线程的执行栈(或叫执行体)都是不一样的,方法本就是在线程的执行栈的啊,
    同 @phx13ye @miaoever
    xinyewdz
        13
    xinyewdz  
       2015-04-28 08:42:37 +08:00   ❤️ 1
    所有方法都存放在方法区,像模板一样。线程调用的时候,这个方法会在线程自己的栈内存执行。
    comicfans44
        14
    comicfans44  
       2015-04-28 09:03:50 +08:00   ❤️ 1
    每个线程都有自己的堆栈(可以理解为局部变量的所在区域),因此一个方法在多个线程上执行的时候,局部变量(也包括HttpServletResponse HttpServletRequest这些由container传递给你的变量)都是特定于线程的,不存在任何冲突的问题。当然也不会有什么"JVM不会让两个线程同时运行"的说法。

    对于servlet来说,只要你不在service方法(或者service间接调用的doPost doGet或任意你自定义的方法)中访问共享的变量(比如servlet的成员变量或者静态成员变量,由于servlet是单实例的),那就完全不需要考虑这个问题。

    servlet的init和destory两个可供重载的方法,由container一次调用,如果有共享的成员变量的初始化和销毁操作,可考虑在这两处操作。

    举例:
    Servlet每被访问一次就打印一行Hello World,多个线程无需访问共享的servlet成员变量,无需考虑同步。
    Servlet记录自己一共被访问多少次,需要一个成员变量记录,则更新此变量时需要考虑同步(可能在多线程中被访问更新)
    khan
        15
    khan  
       2015-04-28 09:18:27 +08:00   ❤️ 1
    线程`独立栈` `共享堆`. 如果在没有上层作用域外部变量的情况下, 俩线程同时调用某个方法貌似不会发生什么. 如果有上层作用域外部变量, 那就是常规的抢占资源问题了. 外部变量的写行为不可预估.
    khan
        16
    khan  
       2015-04-28 09:19:27 +08:00   ❤️ 1
    @ryd994 跟动态语言没啥关系, 你即使用 c 也是这德性
    HelloWorld11
        17
    HelloWorld11  
    OP
       2015-04-28 09:35:31 +08:00
    @helloworld00 应该是吧~~~,这名字也是巧了~ 呼叫 Helloworld,,,
    anexplore
        18
    anexplore  
       2015-04-28 09:42:21 +08:00   ❤️ 1
    说java是动态语言的我也是醉了。。。
    HelloWorld11
        19
    HelloWorld11  
    OP
       2015-04-28 09:56:49 +08:00
    @flaty 就是说虽然是同一个方法,但是线程调用的时候,是拷贝到自己的线程栈执行的么?
    iyangyuan
        20
    iyangyuan  
       2015-04-28 09:58:25 +08:00 via iPhone   ❤️ 1
    java有个名词叫虚拟机栈,是线程私有的,栈上用来放方法帧,方法帧中又包含了局部变量表,所以,如果写操作只针对局部变量,这样是没有任何问题的
    flaty
        21
    flaty  
       2015-04-28 10:21:21 +08:00   ❤️ 1
    @HelloWorld11
    是,,如cpu的缓存,寄存器,,其实是会拷贝那里再操作的.,所以井水不犯河水了.
    HarryZD
        22
    HarryZD  
       2015-04-28 10:21:25 +08:00   ❤️ 1
    机器如果多核,可以运行多线程,每个线程都有自己的方法栈,如果不涉及成员变量的话,方法栈互不干扰
    shuiniushushu
        23
    shuiniushushu  
       2015-04-28 10:35:18 +08:00   ❤️ 1
    方法不怕同时调用,只要你的方法里面没有公共变量
    HelloWorld11
        24
    HelloWorld11  
    OP
       2015-04-28 10:35:48 +08:00   ❤️ 1
    @楼上所有人~ 多谢大家的帮忙哈~~谢谢~
    ryd994
        25
    ryd994  
       2015-04-29 02:26:38 +08:00 via Android
    @khan 有关系
    很多嵌入式C编译器默认是不可重入的,而GNU C库也在很长一段时间里默认不可重入。
    是在大家内存多得用不完,栈空间用不完之后才默认可重入的
    khan
        26
    khan  
       2015-04-30 10:23:32 +08:00
    @ryd994 函数是否可重入取决于编码习惯吧. 例如你在函数中使用了全局变量 会导致并发执行的情况下全局变量的读写不可预估. 这种状况就不可重入.
    反之如果函数中没有互相干扰的访问资源, 则可重入.

    你说的观点我有很多地方不清楚. 还请详细说明, 大家一起讨论讨论
    khan
        27
    khan  
       2015-04-30 10:28:08 +08:00
    @ryd994 你说的情况即使存在, 我认为也不是编译器的原因, 关键因素应该在于 libc 的实现. 所以现在 ISO 组织制定标准时, 都会指明某些函数必须实现可重入 (IEEE Std 1003.1 )
    ryd994
        28
    ryd994  
       2015-04-30 14:14:01 +08:00
    @khan 嵌入式编译器比如sdcc,因资源有限,会默认静态分配所有变量,类似static(但未明确声明static的可能被overlay,即不保证数据保留)
    要可重入,实现的时候就要在栈上分配变量,而不是在编译的时候指定内存地址,这就是区别。
    对嵌入式系统来说,受限于指令集,栈空间通常不足,所以不得已为之
    正常PC平台的编译器没有这个问题,上述只是特例
    hellomsg
        29
    hellomsg  
       2015-05-12 09:54:52 +08:00
    @comicfans44 请问container的源码在哪里能找到,是tomcat、resin他们实现的吗?
    comicfans44
        30
    comicfans44  
       2015-05-12 11:03:25 +08:00
    @hellomsg tomcat是实现之一,jetty也是实现之一。两个都是开源的,源码可以直接拿来参考(resin没用过不知道)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2842 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 13:28 · PVG 21:28 · LAX 05:28 · JFK 08:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.