2016-10-06 39 views
5

让我们以SimpleDateFormat为例,因为它不是线程安全的。java中的volatile和threadLocal

我可以让每个线程使用的ThreadLocal这样它自己的SimpleDateFormat的副本有:

private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ 
    @Override 
    protected SimpleDateFormat initialValue() 
    { 
     return new SimpleDateFormat("yyyyMMdd HHmm"); 
    } 
}; 

但volatile关键字保证一个线程将具有最新的变量的副本。所以我可以不这样做:

volatile SimpleDateFormat myformatter; 

并实现相同的线程安全?

+1

因为这不是线程安全问题发生的地方:线程安全问题是'SimpleDateFormat'具有可变状态,这与引用是否存储在易失性字段中无关。 –

+1

线程局部变量和volatile变量不一样!对于线程本地,每个线程都有一个单独的变量副本。使用volatile,所有线程共享一个变量。像Andy提到的那样,使变量volatile成为线程安全的是因为SimpleDateFormat具有不能由多个线程同时更新的内部状态。 – Jesper

回答

4

volatile关键字保证一个线程将具有最新的可变

的volatile变量只有,并非其字段的副本。如果您需要更改变量的值,则volatile仅适用。在你的使用情况,final看起来会更合适:

private static final SimpleDateFormat format = ... 

这也保证,你将有最新的变量的值 - 因为它只能分配一次它的价值,并static final有保障一旦这个类被完全加载,这个可见性就会被释放


但是,这不是为什么SimpleDateFormat不是线程安全反正理由:它有它用来存储中间值格式的日期时可变状态。

如果一个线程调用format而另一个也是在同一SimpleDateFormatter实例format方法,这些中间变量得到不可预知的跺着脚,导致线程之间的干扰,因此不可预知的输出。

这些中间变量的值在由另一个线程读取/写入时是否是最新的并不重要 - 它们的更新可以是散布的。

简而言之,volatile不会阻止线程干扰,因此不适用于这里的ThreadLocal

+1

'只在volatile变量中,而不是它的字段。“---这实际上是不精确的,甚至是误导性的,因为你会观察写入到volatile字段之前写入线程所做的所有对被引用对象字段的写入。这是安全出版的本质。 –

+0

@MarkoTopolnik真的吗?我想到的东西就像'System.out.println(volatileField.nonVolatileField);'''volatileField''的读取可能会让你知道直到那个时刻所有对'nonVolatileField'的写入;但不能'nonVolatileField'然后在读'volatileField'和'volatileField.nonVolatileField'之间更新? –

+0

是的,这就是为什么我说你的措词是“误导”而不是“不正确”。这听起来像你没有_any_保证除了'volatile'引用本身。另一方面,你不能保证观察volatile字段的“最新”状态,因为“最近的”在JMM中甚至不是一个明确定义的概念。本质区别在于顺序一致性与线性化。 –

0

由于SimpleDateFormat的相同实例将在不同线程中使用,因此您无法实现与volatile相同的线程安全。

1

ThreadLocal是一个使线程拥有自己本地副本的对象的设施。 ThreadLocals最适用于线程安全的线程安全策略线程约束(即使对于非线程安全的多个对象,线程安全的使用仍然是可能的,只要没有引用它们就可以泄漏出来限制线程)。对于在实例化它们的线程之外共享的可变对象,ThreadLocals不能帮助线程安全地使用它。

volatile关键字用于提供线程安全性的弱形式,其中包含可以被许多不同线程访问的变量。一个关键的区别是ThreadLocals通常不会被多个线程访问。从广义上讲,线程安全需要可见性(变量的最新更新应该对其他线程可见)和互斥(状态转换必须是原子的,以便状态不能被观察为不一致)。 Volatile与Java内存模型一起工作以保证变量可见,但它不提供互斥形式,因此不提供对象中状态转换的原子性。

因为volatileThreadLocal是如此不同,所以实际上没有什么可以替代另一个的普通情况。