java中的静态变量和多线程

时间:2011-12-08 14:15:11

标签: java multithreading static

类的静态成员是否只是每个进程或线程的单个实例?含义是否每个线程都有自己的类的静态成员变量的副本?

我的猜测是按程序进行的,我是否正确?

7 个答案:

答案 0 :(得分:40)

static个字段每个类加载器有一个值,但我认为您的问题的主要内容如下:

  

每个线程都有自己的类

的静态成员变量的副本

虽然魔鬼在细节中,但这是正确的。每个线程可以在其自己的本地内存空间/缓存中拥有它自己的字段副本,除非该字段已标记为volatile,这会强制字段被内存屏障包围,从而导致每次访问时的内存同步/更新

如果没有volatile,对static字段的任何更新和读取都将对本地线程存储进行,并且只有在线程穿过内存屏障时才会更新。如果没有内存障碍,则无法保证数据操作的顺序以及何时与其他线程共享更新。

这是一个关于Java memory model和好overview of some of the challenges的好网页。

答案 1 :(得分:24)

静态字段为每个类加载器提供了一个值。

如果您需要每线程值,请创建一个静态ThreadLocal<T>

答案 2 :(得分:10)

每个类加载器都有一个static变量的副本,它加载了这个类。这或多或少意味着每个过程,但是你需要意识到差异。

E.g。当两个web-apps捆绑相同的类时,该类将被加载两次,因此具有相同static字段的两个副本。

如果您需要基于线程的具有独立值的变量,请查看ThreadLocal

答案 3 :(得分:4)

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html

  

类变量(静态字段)类变量是使用static修饰符声明的任何字段;这告诉编译器这个变量只有一个副本存在,无论该类被实例化多少次。定义特定类型自行车的齿轮数量的区域可以标记为静态,因为从概念上讲,相同数量的齿轮将适用于所有实例。代码static int numGears = 6;会创建这样一个静态字段。此外,可以添加关键字final以指示齿轮的数量永远不会改变。

线程与此无关。 (另一方面,类加载器也是如此。但是,如果您在应用程序中使用多个类加载器,那么您可能已经明白了这一点)。

答案 4 :(得分:2)

线程问题是这样的:Java内存的“10,000英尺”视图是所有类,所有对象,所有类加载器以及正在运行的JVM中的所有线程共享的单个内存块 - - 代码中某个地方可以访问的任何地方都可以访问其他任何地方(给定适当的参考)。唯一的例外是寄存器和执行堆栈,它们在概念上基于每个线程。

这对于单个处理器非常有用,其中线程轮流在一组寄存器和ALU等中执行。但是大多数现代计算机都有多个处理器,因此几个线程可以同时执行。如果这些处理器必须全部引用相同的物理内存(基于实际经验),您只能通过4个处理器获得大约1.5倍的性能提升,并且它会从那里退化。

因此使用“缓存”,因此每个处理器都有自己的小型私有副本,包含更大的内存。大多数情况下,处理器正在处理完全不同的内存区域,所以这种方法很好,但偶尔(当处理一些“共享”对象时)它们必须在同一组字节上“战斗”。

解决方案是建立协议,这样任何两个处理器都不会尝试同时(或几乎相同)修改相同的存储位置,并确保一个处理器的更改被“刷新”到主存储器和其他处理器了解更改并建议重新加载他们对修改数据的看法。

但是在每次操作之后执行此操作都是非常低效的(而且,坦率地说,硬件设计人员已经在很大程度上避开了这个问题,并将更多的工作推到了软件上,而不是可能是合理的)。所以使用这样的方案,使得数据的刷新和重载仅发生在某些“边界”上,或者当某些特殊类型的引用完成时。

请注意,所有这些与变量是否为“静态”无关,也与对象是否“不可变”无关。它是现代多处理器硬件架构中固有的,与Java线程模型相结合。

答案 5 :(得分:0)

由于SLaks建议每个JVM使用一个,但要注意保持锁定在同一个静态引用上的两个线程会相互阻塞,因此每个vm只有一个这样的引用。但它们不会阻止引用实例变量的线程。

答案 6 :(得分:-2)

实际上类变量对应于类,它只共享单个内存,因此每个类实例所做的更改可以更改类变量。