2012-09-03 41 views
14

据我所知java.util.Date是可变的,所以如果多线程试图访问和修改它,它就不是线程安全的。我们如何使用客户端锁定或组合(包装)来使其线程安全?如何使Java.util.Date线程安全

+4

如果我们已经在这里,'GregorianCalendar'和'SimpleDateFormat'也不是线程安全的。总是值得提醒。 –

+0

感谢提醒 – peter

回答

28

按照此顺序,从最好到最差:

  1. 不使用它在所有的,看看

  2. 不使用它在所有使用AtomicLong或一成不变的原始longvolatile代表时代

  3. 封装它。总是返回Date的防御副本,从不参考内部对象

  4. 同步于Date实例。

+0

我想知道你为什么认为第二个比第三个好? – peter

+2

@ user1389813:好问题! 1.基元是不可变的,因此隐式线程安全。来自“简明英汉词典”巧合的是,你不能重新引用内部对象而不是防御性副本。 3.更轻量,更少复制(不是很重要)。但我同意,2和3都很好。同样显而易见的是'Date'具有比'long'更好的语义。 –

+0

我明白了。第二点你需要守卫它与锁权利?因为@dystroy指出增量操作不是原子的。 – peter

-1

没有简单的解决方案来创建Date类的线程安全封装。最好的方法是使用​​块同步它的所有对象。

+0

然后你需要携带这个同步块,无论你使用它。这在实践中不是很好吗? – peter

+0

代码很糟糕。这就是为什么Tomasz的回答比我的要好得多;) –

2

最简单的解决方法是永远不要修改日期并永远不会共享它。即只对本地变量使用日期。

您可以使用JodaTime,因为它具有不可变的日期对象。

3

您可以使用long值(Epoch以来的毫秒数)而不是Date实例。分配它将是一个原子操作,它始终是连贯的。

但是,您的问题可能不是日期值本身,而是整个算法,这意味着真正的答案将基于您真正的问题。

这里的越野车运行在多线程上下文中的例子:

long time; 
void add(long duration) { 
    time += duration; 
} 

这里的问题是,你可能有导致只有一个有效的另外两个平行的增加,因为time += duration不是原子(它真的time=time+duration)。

使用long而不是可变对象是不够的。在这种情况下,您可以通过将函数设置为同步来解决问题,但其他情况可能会更棘手。

+0

你的意思是'你的问题可能不在数据值本身上,而是在整个算法上 – peter

+0

如果你'重新开始阅读日期并通过替换值完成长时间的操作。在操作过程中锁定值可能会做到这一点。 –

+0

它会帮助,如果它变得易变? – peter