java - 为不安全发布的 java.lang.String 提供恢复时间

标签 java multithreading concurrency thread-safety safe-publication

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 之前不传递对其自身的引用字段,它始终是安全发布的。您无需担心hashhash32字段也可以。它们未安全发布,但只能具有值 0 或有效的哈希码。如果它们仍然是 0,则方法 String.hashCode将重新计算该值 - 它只会导致其他线程重新计算 hashCode(当这已经在其他线程中早先完成时)。

引用 myPathMultiThreadingClass未安全发布,因为它不是 final 。 “此时 MultiThreadingClass 正在使用其构造函数进行初始化”,但稍后,在构造函数完成后,运行构造函数的线程之外的其他线程可能会看到值 nullmyPath而不是对您的字符串的引用。

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/

相关文章:

java - Aws Lambda- “errorMessage”:可用 “No bean named 'EntityManagerFactory”

java - HashMap 未填充

multithreading - 使用 TDD 驱除线程安全代码

java - 从位置类获取 "getDistance"并使用 if 条件在目标类中使用它

c# - 有没有办法强制应用程序以单线程方式运行?

c++ - linux中的pthread_mutex_t是否可重入(如果一个线程试图获取它已经持有的锁,则请求成功)

python 线程并行运行?

ember.js - 如何根据promise返回数据

java - JavaFX 中的实时更新折线图

java.lang.RuntimeException : Unable to start activity ComponentInfo Android Manifest 错误