2

在阅读线程安全时,我遇到了这个问题。如果我的方法是正确的,则本地基元和对象引用存在于堆栈中,而堆栈内的引用指向的实际对象存在于堆中。

但是当涉及到方法本地非原始对象初始化时,这不会导致并发问题吗?我的意思是,如果方法 locals 非原语存在于堆中并且只有指针存在于堆栈中,它与实例变量不一样吗?

有人可以帮我理解这个....

附言

想想两个线程,每个线程都有自己的两个堆栈和一个堆。我的理解是这两个线程将它们的方法本地原始变量保存在它们的堆栈中。我对此没有意见。

但是如果我们有一个带有非原始方法局部变量的方法呢?那么如果该变量的对象存储在堆内,两个线程都可以访问同一个对象,不是吗?因此,如果是这种情况,就会出现同步问题。

这就是我要问的。

谢谢

4

5 回答 5

3

但是如果我们有一个带有非原始方法局部变量的方法呢?那么如果该变量的对象存储在堆内,两个线程都可以访问同一个对象,不是吗?因此,如果是这种情况,就会出现同步问题。

我想知道为什么你会认为这两个引用将引用同一个对象。

引用的对象的创建是通过new显式完成的(或者其他类似的方法,但是思路是一样的)

因此,与 C++ 不同,如果您在 Java 中声明

Foo foo;

没有实例化 Foo 对象。 foo只是一个指向任何东西的指针。

这将在堆中为您创建一个 Foo 对象实例。

Foo foo = new Foo();

如果有两个线程在运行这段代码,线程 1 会Foo在栈中有一个引用,并要求Foo在堆中分配一个新对象,然后将该Fooobj 的地址分配给该引用foo。线程 2 也在做同样的事情。请注意,线程 2 也要求分配一个新Foo对象,它将是与线程 1 分配的对象不同的对象。

这是基本的(并且非常简化)的想法。

于 2012-07-27T07:32:25.997 回答
2

如果两个线程都具有对该对象的引用,则它们都可以访问同一个对象。如果您有如下方法:

public String concat(String a, String b) {
    StringBuilder builder = new StringBuilder();
    builder.append(a);
    builder.append(b);
    return builder.toString();
}

StringBuilder 对象确实在堆中,但是只有一个线程引用了这个对象。没有其他线程可以引用此 StringBuilder。所以它本质上是线程安全的。

相反,如果您有以下情况:

public String concat(String a, String b) {
    final StringBuilder builder = new StringBuilder();
    new Thread(new Runnable() {
        @Override
        public void run() {
            builder.append("haha!");
        }
    }).start();
    builder.append(a);
    builder.append(b);
    return builder.toString();
}

然后你有一个线程安全问题,因为你将本地创建的对象引用传递给另一个线程,而 StringBuilder 不是线程安全的。

于 2012-07-27T07:05:15.723 回答
1

But what if we have a method with non primitive method local variables ? Then if the object for that variable is stored inside the heap, both the threads will have the access to the same object, won't they ? So if that's the case there would be Sync problems

您部分回答了您自己的问题。该引用值存储在堆栈中,但实际对象内容存储在堆中,当您调用 new Object() 时,每个线程都会创建不同的新对象,这些新对象将存储在堆中,每个线程访问它使用存储在自己的堆栈中的引用值创建的对象

于 2012-07-27T08:59:08.400 回答
0

局部变量要么是原语,要么是对在别处创建的对象的引用(如果你进行了赋值),要么是对新创建的对象的引用(使用“new”运算符)

正如您所说,对于第一种情况,没有问题。

对于最后一种情况,当您在本地创建一个新对象时,每次调用都会创建一个新对象,因此不会出现并发问题,因为每次调用都会在堆中创建一个对象

但是对于第二种情况,由于对象是在其他地方创建的,因此您必须考虑并发性

于 2012-07-27T07:04:03.917 回答
0

就我的想法,可能是您的困惑点:堆不像堆栈那样管理。它是共享的,是的 - 因为所有线程创建的对象都在堆中。然而,当每个对象被创建时,它在堆中被赋予了一个唯一的位置/空间。两个线程上的两个方法同时运行并创建一个对象实例将在共享堆中创建明显不同的对象。

它们是在这个共享堆中创建的,因此如果方法foo返回对象引用,或者存储它,或者调用另一个间接存储它的方法......它在返回时不会被销毁foo并且堆栈被弹出。

拥有垃圾收集器的神奇之处在于,您不必跟踪这些“东西”并在将来的某个适当时间自行销毁它。使您的代码保持简单,让您专注于算法(或学习编程)。但我离题了...

于 2012-07-27T07:38:19.610 回答