我在类中有两个共享的可变对象,其线程安全策略已定义为"线程安全。"
public static final GregorianCalendar CAL = new GregorianCalendar();
public static final SimpleDateFormat SDF = new SimpleDateFormat();
目的是减少对象创建的数量,因为这些对象的创建成本很高,并且需要经常调用需要使用它们的方法。
这是一个这样的(静态工厂)方法:
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);
}
我还为此课程设置了一项政策,即lockCal
之前必须始终获取lockSdf
。然而,这个类也是如此:
因为SDF依赖于CAL,所以我想知道单独锁定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);
}
答案 0 :(得分:1)
如果SDF
仅被已获得lockCal
的线程使用,则它一次只能被一个线程访问,即使你删除了锁也是线程安全的在lockSdf
。
如果您选择依赖此观察,则应该清楚地记录,以便将来的维护程序员不会在SDF
之外开始使用synchronized (lockCal)
。
答案 1 :(得分:0)
一般来说,此处不保证线程安全。假设SDF应该被lockSDF保护,但是它在不同的锁下被修改,如果它只获取了lockSDF,则另一个线程可能看不到SDF更改的结果。 但是你有一个策略:在lockSdf之前获取lockCal。看起来像"有点"解决了这个问题,但是
1)它使线程安全的推理太难了
2)它使lockSdf无用
假设SDF依赖于CAL而CAL由lockCal保护,那么使用lockCal来保护SDF也是有意义的。
实践中的Java并发(Brian Goetz):
第1部分摘要
...
使用相同的锁保护不变量中的所有变量。
...