JAVA线程(不同堆栈)同步

我有一个关于由多个线程执行的代码同步的问题:

据我所知,每个线程都有自己的堆栈,因此,非静态变量存在于每个线程的内存中的不同位置(对于X线程,存在包含所有非静态变量的X堆栈)。 那么为什么需要同步任何东西呢?

我的意思是,如果线程执行的代码包含一些类变量v1,那么每个线程都有自己的v1实例(不同的内存地址),没有其他线程可以“触摸”它…是不是这样?

非静态变量存在于每个线程的内存中的不同位置

这不是真的,所以答案是

如果线程执行的代码包含一些类变量v1,那么每个线程都有自己的“实例”v1(不同的内存地址),没有其他线程可以“触摸”它…是不是这样

没有。 线程可以触及由其他线程分配和修改的对象实例,并且程序员负担过重以确保这不会影响程序的正确性。

类成员变量存在于每个类实例的内存中的单个位置,而不是每个线程。 确实,在内存屏障 (认为​​是synchronized的start {和end } )之间,线程可能具有对象状态的缓存,但这与强制每线程存储的语言不同。 “每个线程的内存”是它的堆栈,它不包含对象成员* – 只引用对象。

想到它的最好方法是堆上有一个位置,每个对象,但可能有多个读取和写入涉及同时发生的内存位置。

如果您听说线程在堆的不同部分分配对象,我可以看到您将如何得出您所做的结论。 某些JVM具有优化function,可以进行线程局部分配,但不会阻止其他线程访问这些对象。

线程局部分配

如果分配器是真正实现的,如清单1所示,共享heapStart字段将很快成为一个重要的并发瓶颈,因为每个分配都涉及获取保护该字段的锁。 为了避免这个问题,大多数JVM使用线程局部分配块,其中每个线程从堆中分配更大的内存块,并按顺序从该线程局部块中提供小的分配请求。 因此,线程必须获取共享堆锁的次数大大减少,从而提高了并发性。

* – JVM优化可能允许在堆栈上分配一些对象。

堆栈是线程安全的,而堆不是线程安全的,除非您同步代码。 堆栈包含局部变量和方法参数(原始和引用),而堆包含对象。

堆栈是(想想一个调用堆栈,局部变量),但是类变量存在于堆中,你必须同步访问它们:)

只保证在堆栈上分配原始类型,例如int 。 对象和数组通常都存储在堆中,除非Escape Analysis确定对象的范围仅限于过程的范围 ”。

在同一个对象实例上 ,如果您的方法未同步,则无法保证相同的代码不会在不同的线程中执行两次 – > havoc! 哪个是正确的价值?

至少,您希望将访问变量的方法声明为synchronized。 如果您想要更精细的控件,可以使用ReentrantReadWriteLock

声明一个方法同步在对象实例上同步,所以这是安全的。

局部变量,基元和引用是隐式线程局部的。 但是,引用的对象可以共享,当线程可以修改共享对象时,很可能需要synchronised ,锁定或其他策略来确保线程安全。

一些关键点可以帮助澄清您的疑虑 –

  1. 对象始终在堆上分配。

  2. 类级别变量在线程之间共享 (同一对象的线程)

  3. 局部变量总是线程安全的(如果没有以非线程安全的方式暴露给外部世界)

“非静态变量存在于不同的位置”可能不正确。 在Java中,您永远不会直接了解“堆栈”的任何内容。 所有类变量(静态或实例)都来自堆。 但是,作为一名java开发人员,您并不真正关心它。

你不关心线程安全的唯一一次是你的类是不可变的(在构造之后不要改变)或者你没有在线程中做任何事情。 如果您的课程不属于这两个类别,您需要考虑使它们成为线程安全的。

您可以在设计中获得的不变性越多,线程问题就越容易推理和克服。

Nrj有正确的想法。