java.lang.String 只是有效地不可变。 《Java 并发实践》的 Brian Goetz 表示,像有效不可变对象(immutable对象)这样的东西只有在安全发布时才是线程安全的。现在,假设我像这样不安全地发布字符串:
public class MultiThreadingClass {
private String myPath ="c:\\somepath";
//beginmt runs simultaneously on a single instance of MultiThreading class
public void beginmt(){
Holder h = new Holder();
h.setPath(new File(myPath)); //line 6
h.begin();
}
}
public class Holder {
private File path;
public void setPath(File path){
this.path = path;
}
public void begin(){
System.out.println(path.getCanonicalPath()+"some string");
}
}
当 MultiThreadingClass 使用其构造函数进行初始化时,第 6 行的 File 构造函数可能看不到 myPath 的值。
然后,在构造不安全发布的 String 对象后大约三秒,MultiThreadingClass 上的线程仍在运行。 File 构造函数是否还有可能看不到 myPath 的值?
最佳答案
您提出问题的陈述:
At the moment that the MultiThreadingClass is initializing with its constructor, it could happen that the File constructor on line 6 may not see the value of myPath.
答案很复杂。
您无需担心字符数组 value
里面String
目的。正如我在评论中提到的,因为它是 final
在构造函数中分配的字段,并且因为 String
在分配 final
之前不传递对其自身的引用字段,它始终是安全发布的。您无需担心hash
和hash32
字段也可以。它们未安全发布,但只能具有值 0 或有效的哈希码。如果它们仍然是 0,则方法 String.hashCode
将重新计算该值 - 它只会导致其他线程重新计算 hashCode(当这已经在其他线程中早先完成时)。
引用 myPath
在MultiThreadingClass
未安全发布,因为它不是 final
。 “此时 MultiThreadingClass 正在使用其构造函数进行初始化”,但稍后,在构造函数完成后,运行构造函数的线程之外的其他线程可能会看到值 null
在myPath
而不是对您的字符串的引用。
Java Language Specification 的 Java 内存模型部分有一个示例[版本 8 已链接,但自从 JMM 在 JSR-133 中发布以来,这一点没有改变]:
Example 17.5-1. final Fields In The Java Memory Model
The program below illustrates how final fields compare to normal fields.
class FinalFieldExample { final int x; int y; static FinalFieldExample f; public FinalFieldExample() { x = 3; y = 4; } static void writer() { f = new FinalFieldExample(); } static void reader() { if (f != null) { int i = f.x; // guaranteed to see 3 int j = f.y; // could see 0 } } }
The class FinalFieldExample has a final int field x and a non-final int field y. One thread might execute the method writer and another might execute the method reader.
Because the writer method writes f after the object's constructor finishes, the reader method will be guaranteed to see the properly initialized value for f.x: it will read the value 3. However, f.y is not final; the reader method is therefore not guaranteed to see the value 4 for it.
这甚至可能发生在具有许多线程的重负载机器上。
解决方法/解决方案:
- 制造
myPath
一个final
字段(并且在分配字段之前不要添加传递this
引用的构造函数) - 制造
myPath
一个volatile
字段 - 确保所有线程都访问
myPath
在访问myPath
之前在同一监视器对象上同步。例如,通过创建beginmt
一个synchronized
方法或任何其他方式。
关于java - 为不安全发布的 java.lang.String 提供恢复时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25438681/