Java 此代码是否需要锁嵌套?
我在一个类中有两个共享的可变对象,其线程安全策略被定义为“线程安全” 这样做的目的是减少对象创建的数量,因为创建这些对象的成本很高,而且需要使用它们的方法预计会被频繁调用 以下是一种(静态工厂)方法: 我还为此类设置了一个策略,即必须在Java 此代码是否需要锁嵌套?,java,concurrency,synchronization,thread-safety,Java,Concurrency,Synchronization,Thread Safety,我在一个类中有两个共享的可变对象,其线程安全策略被定义为“线程安全” 这样做的目的是减少对象创建的数量,因为创建这些对象的成本很高,而且需要使用它们的方法预计会被频繁调用 以下是一种(静态工厂)方法: 我还为此类设置了一个策略,即必须在lockSdf之前获取lockCal。然而,这一类也是如此: CAL可以在没有SDF的情况下锁定和使用,在这种情况下SDF未锁定 除非也使用CAL,否则方法中永远不会使用SDF 由于SDF依赖于CAL,我想知道仅锁定lockCal是否足以防止并发访问期间的数据
lockSdf
之前获取lockCal
。然而,这一类也是如此:
- CAL可以在没有SDF的情况下锁定和使用,在这种情况下SDF未锁定
- 除非也使用CAL,否则方法中永远不会使用SDF
lockCal
是否足以防止并发访问期间的数据不一致。这将允许我免除对SDF的锁定。换句话说,如果我只使用以下工具,在上述条件下,线程安全性是否仍然得到保证:
public static MJD ofTimeStampInZone(String stamp, String form, TimeZone tz) {
double result;
synchronized(lockCal) {
CAL.setTimeZone(tz);
SDF.setCalendar(CAL);
SDF.applyPattern(form);
try {
Date d = SDF.parse(stamp);
CAL.setTime(d);
result = (CAL.getTimeInMillis() / (86400.0 * 1000.0)) +
POSIX_EPOCH_AS_MJD;
}
catch (ParseException e)
{ throw new IllegalArgumentException("Invalid parsing format"); }
}
return new MJD(result);
}
一般来说,这里不保证线程安全。假设SDF应该由lockSDF保护,但它在不同的锁下被修改,如果另一个线程只获取lockSDF,则可能看不到SDF更改的结果。 但你有一个政策:在lockSdf之前获得lockCal。看起来它“某种程度上”解决了问题,但是 1) 这使得关于线程安全性的推理变得过于困难 2) 它使锁变得无用 假设SDF依赖于CAL并且CAL由lockCal保护,那么也可以使用lockCal保护SDF 实践中的Java并发(Brian Goetz): 第1部分概述 使用相同的锁保护不变量中的所有变量
如果
SDF
仅由已获取lockCal
的线程使用,则一次只能由单个线程访问它,即,即使移除lockSdf
上的锁,它也是线程安全的
如果您选择依赖此观察结果,您应该清楚地记录它,这样未来的维护程序员就不会在
同步(lockCal)
之外开始使用SDF
您是否有任何其他代码在lockCal
和lockSdf
上使用双重锁定?是,我还有另外两种方法可以锁定lockCal
,然后锁定lockSdf
。我有几种方法只锁定lockCal
,但这些方法不会改变SDF
的状态,请发布相关的代码片段。您不需要向我们展示所有内容,只需展示一些与您为ofTimeStampInZone
展示的内容大致相同的内容,其线程安全策略已定义为“线程安全”-但是这些类不是线程安全的:@Ruslan:没有要求线程安全类不能由本身不是线程安全的组件组成(事实上,线程安全类总是由可变的、非线程安全的类(例如ArrayList)组成)。我不确定我是否理解你的答案。当由lockCal保护时,您对SDF的潜在可见性问题提出了一个重要观点。但是你是说如果SDF依赖于CAL,那么用同一个锁锁定两个可变对象是可以接受的吗?@scottb,是的,这是可以接受的,实际上这是推荐的方法。我用一句话更新了我的答案。
public static MJD ofTimeStampInZone(String stamp, String form, TimeZone tz) {
double result;
synchronized(lockCal) {
synchronized(lockSdf) {
CAL.setTimeZone(tz);
SDF.setCalendar(CAL);
SDF.applyPattern(form);
try {
Date d = SDF.parse(stamp);
CAL.setTime(d);
result = (CAL.getTimeInMillis() / (86400.0 * 1000.0)) +
POSIX_EPOCH_AS_MJD;
}
catch (ParseException e)
{ throw new IllegalArgumentException("Invalid parsing format"); }
}
}
return new MJD(result);
}
public static MJD ofTimeStampInZone(String stamp, String form, TimeZone tz) {
double result;
synchronized(lockCal) {
CAL.setTimeZone(tz);
SDF.setCalendar(CAL);
SDF.applyPattern(form);
try {
Date d = SDF.parse(stamp);
CAL.setTime(d);
result = (CAL.getTimeInMillis() / (86400.0 * 1000.0)) +
POSIX_EPOCH_AS_MJD;
}
catch (ParseException e)
{ throw new IllegalArgumentException("Invalid parsing format"); }
}
return new MJD(result);
}