java中的静态变量和multithreading

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

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

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

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

如前所述, static字段每个类加载器有一个值。 但是我想我会对你问题的以下文字发表评论:

每个线程都有自己的类的静态成员变量的副本

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

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

这是关于Java内存模型的一个不错的页面,以及对一些挑战的良好概述 。

每个类加载器都有一个static变量副本,它加载了这个类。 这或多或少意味着每个进程,但您需要了解其中的差异。

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

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

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

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

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

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

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

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

解决方案是建立协议,以便没有两个处理器会同时(或几乎相同)尝试修改相同的存储位置,并确保一个处理器的更改被“刷新”到主存储,并使其他处理器知道更改并建议重新加载他们对修改数据的视图。

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

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

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

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