Java静态工厂创建非线程安全对象

evo*_*ved 0 java multithreading thread-safety static-initializer

在《清洁代码》一书中有一个我想更好地理解的例子:

public static SimpleDateFormat makeStandardHttpDateFormat() {
    // SimpleDateFormat is not thread safe, so we need to create each instance independently
    SimpleDateFormat df = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z");
    df.setTimeZone(TimeZone.getTimeZone("GMT"));
    return df
}
Run Code Online (Sandbox Code Playgroud)

我读到这SimpleDateFormat不是线程安全的,因为它:将中间结果存储在实例字段中。因此,如果一个实例被两个线程使用,它们可能会弄乱彼此的结果。现在我感兴趣的是为什么使用静态工厂方法是避免此类SimpleDateFormat非线程安全类的线程安全问题的一种解决方案(可能不是最好的)?

如果我们有两个线程 A 和 B,为什么创建makeStandardHttpDateFormat()静态方法会有帮助呢?makeStandardHttpDateFormat()如果不是静态的,因为我们为每个线程创建了无论如何的新实例,那不是一样吗SimpleDateFormat

书中指出

……这个评论是完全合理的。它将防止一些过于急切的程序员以效率的名义使用静态初始化程序。

静态方法真的那么慢吗?这句话是什么意思呢?为什么仅仅因为注释就应该阻止“过于热切的程序员”使用这个静态方法?IDE 可能甚至不会显示注释。但是,它可能会表明该方法是静态的。所以对我来说这个评论毫无意义。至少,不像书中提到的那样。

Mic*_*ael 5

没有任何关于静态的东西,或者关于工厂方法的任何东西,可以神奇地使一些本来不是线程安全的东西。

我假设他试图推荐的策略是,每次一段代码想要SimpleDateFormat代表该格式的 a 时,而不是依赖于周围的某个实例(可以由任意数量的线程使用),那么它应该调用这个工厂方法代替。即几乎每个SimpleDateFormat都会被使用一次,然后被垃圾收集。

我不确定他为什么要专门将其与静态初始化程序进行对比。我想这就是他的想法。由于多个线程可以访问FORMAT,因此任何访问都不是线程安全的。

class Foo {
    private static final SimpleDateFormat FORMAT = new SimpleDateFormat();

    static {
        // Uncle Bob disapproves
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
    } 
}
Run Code Online (Sandbox Code Playgroud)

工厂方法是一种策略,但并不是一个很好的策略。它确保每个按makeStandardHttpDateFormat预期依赖的调用者都是线程安全的,但它不保证每个调用者都会依赖makeStandardHttpDateFormat.

更好的策略是使用一些不可变的变体,例如DateTimeFormatter.