我一直在阅读Java多线程。我遇到了跨线程here的资源共享主题。它让我对本地对象引用感到困惑。
对象的本地引用略有不同。引用本身不共享。但是,引用的对象不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。 如果本地创建的对象永远不会转义它创建的方法,那么它是线程安全的。实际上,只要这些方法或对象都没有生成,就可以将它传递给其他方法和对象。传递的对象可用于其他线程。
前几行我能理解,但作者对上面粗线的意思是什么?更具体地说,他在本地逃避的意思是什么?
有些人请注意解释,可能有不同的例子。
答案 0 :(得分:2)
在此测试中,HashSet的使用是线程安全的,因为对它的唯一引用是在局部变量中,如果另一个线程进入此方法,它将创建一个新的Set。
void x() {
Set s = new HashSet();
s.add(1);
}
但是如果我们在字段中保存对HashSet的引用,那么另一个线程可以同时更改它
Set s;
void x() {
s = new HashSet();
s.add(1);
}
void y() {
s.add(2);
}
答案 1 :(得分:1)
“线程安全”意味着不可能将对象置于不正确的状态,或者由两个线程同时修改相同数据引起某些其他错误;见例如。 What does threadsafe mean?和What is meant by thread-safe code?。
本地创建的对象,即您在方法中创建并存储在该方法的局部变量中的对象,通常只能由该方法中的代码访问。当对象被调用方法return
或作为参数传递给另一个方法时(包括调用对象本身的方法时),这些方法可以访问它,并且该对象被认为具有转义。然后,这些方法可能会将其发布到其他线程可以看到它并尝试修改它的位置,从而可能导致线程安全问题。但是,如果一个对象永远不会逃脱,那么可以保证没有其他线程可以访问它,因此您对该对象的使用将始终是线程安全的。
答案 2 :(得分:1)
首先,在方法中创建的所有本地引用,它们都存储在方法本地堆栈中 - 因此它们是线程安全的。但是,您在任何方法中创建的对象仍将在共享堆空间中分配。但是,由于您的局部变量指向该对象,因此没有其他变量可以访问它 - 除非您将句柄分配给其他线程(或任何其他非线程安全引用)的其他变量
请在下面找到解释的尝试:
public class Test {
Map<String, String> stringMap = null;
public void method1() {
Map<String, String> method1Map = new HashMap<String, String>();
// method1Map.<some method>
// map is thread safe till now
method2(method1Map);
}
public void method2(Map<String, String> map) {
// map.<some method>
// map is still thread safe till now
// after the below line the map is no more thread safe as the instance
// variable got the handle to same map
this.stringMap = map;
}
}
答案 3 :(得分:0)
该方法可以由不同的线程调用。并且将为每个调用和每个线程创建一个新实例。但是,没有一个创建的实例可以共享给另一个线程,该实例仅存在于本地方法堆栈中。
public void someMethod(){
//First and second thread calls method, both get there own object
LocalObject localObject = new LocalObject();
localObject.callMethod();
method2(localObject);
}
//calling next method, own object, too. Instance of the method stack of thread one or two
public void method2(LocalObject localObject){
localObject.setValue("value");
}
考虑相反的情况,该类的实例成员。两个线程可以调用方法
InstanceObject instObject;
public void someMethod(){
//First and second thread calls method, both initialize the member var
if(instanceObject == null)
instObject = new InstanceObject ();
instObject.callMethod();
method2(localObject);
}
public void method2(){
instObject.setValue("value");
}
现在这不是线程安全的。线程一调用someMethod并开始初始化instObject。线程二稍后会出现。对于线程2,instObject也为null。这可能是因为两个原因:
instObject没有完全初始化
instObject的值不会从线程1的内存传递到线程2的内存
当线程1更改instObj时,它期望对象具有特定值。但是比第二个线程更改了这个值。或者 - 甚至更糟 - 线程1和线程2更改了instObject,但更改仅存在于特定线程的内存中。