在阅读Thread Safety时,我遇到了这个问题。 如果我是正确的方法,则局部基元和对象引用存在于堆栈中,并且堆栈内的引用指向的实际对象存在于堆中。
但是当谈到方法本地非原始对象初始化时,这不会导致并发问题吗?我的意思是如果方法locals非原语存在于堆中,只有指针存在于堆栈中,是不是与实例变量相同?
有人可以帮我理解这个....
PS
想想两个线程,每个线程有两个自己的堆栈和一个堆栈。我理解的是两个线程将它们的方法本地原始变量保存在它们的堆栈中。我没有问题。
但是如果我们有一个非原始方法局部变量的方法呢?然后,如果该变量的对象存储在堆内,则两个线程都可以访问同一个对象,不是吗?因此,如果是这种情况,则会出现同步问题。
这就是我的要求。
谢谢
答案 0 :(得分:2)
但是如果我们有一个非原始方法局部变量的方法呢? ?然后,如果该变量的对象存储在堆内,则两者都存在 线程将有权访问同一个对象,不是吗?所以 如果是这种情况会出现同步问题。
我想知道为什么你会认为这两个引用会引用同一个对象。
引用的对象的创建是由new(或其他类似方法,但想法是相同的)明确完成的。
因此,与C ++不同,如果您在Java中声明这一点
Foo foo;
没有实例化Foo对象。 foo
只是一个指向任何东西的指针。
这将在堆中创建一个Foo对象实例。
Foo foo = new Foo();
如果两个线程正在运行这段代码,则线程1将在堆栈中具有Foo
引用,并要求在堆中分配新的Foo
对象,然后分配该Foo
的地址{1}} obj参考foo
。线程2正在做同样的事情。请注意,线程2也要求分配一个新的Foo
对象,它将与分配线程1的对象不同。
这是基本的(也是简化的)想法。
答案 1 :(得分:1)
如果两个线程都具有对该对象的引用,则它们都可以访问同一个对象。如果您有类似以下的方法:
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不是线程安全的。
答案 2 :(得分: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()时,每个线程都会创建不同的新对象,这些对象将存储在堆中,每个线程使用存储在其自己的堆栈中的引用值访问它创建的对象
答案 3 :(得分:0)
局部变量可以是基元,对其他地方创建的对象的引用(如果进行分配),也可以是对新创建的对象的引用(使用“new”运算符)
对于第一种情况,如你所说,没有问题。
对于最后一种情况,当你在本地工作一个新对象时,每次调用都会创建一个新对象,因此没有并发问题,因为每次调用时堆中都会有一个对象
但对于第二种情况,由于对象已在其他地方创建,因此您必须考虑并发性
答案 4 :(得分:0)
只是想知道可能是你的混淆点:堆不像堆栈一样管理。它是共享的,是的 - 因为所有线程创建的对象都在堆中。但是,由于每个对象都是创建的,因此它在堆中被赋予了唯一的位置/空间。并发运行并创建对象实例的两个线程上的两个方法将在共享堆中创建截然不同的对象。
它们是在这个共享堆中创建的,这样如果方法foo
返回对象引用,或者存储它,或者调用另一个间接存储它的方法......当{{1}时它不会被销毁返回并弹出堆栈。
拥有垃圾收集器的神奇之处在于,您不必跟踪这些“东西”并在将来的某个适当时刻自行销毁它。保持代码简单,让您专注于算法(或学习编程)。但我离题了......