此代码是否需要嵌套锁?
Is nesting of locks necessary for this code?
我在 class 中有两个共享可变对象,它们的线程安全策略已定义为 "thread safe."
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);
}
我还为此 class 设置了一个策略,即必须始终在 lockSdf
之前获取 lockCal
。然而,这也是如此 class:
- CAL 可以在没有 SDF 的情况下锁定和使用,在这种情况下 SDF 不会被锁定。
- 除非同时使用 CAL,否则方法中永远不会使用 SDF
因为 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);
}
一般来说,这里不保证线程安全。假设 SDF 应该由 lockSDF 保护,但它在不同的锁下被修改,如果另一个线程仅获取 lockSDF,它可能看不到 SDF 更改的结果。
但是您有一个策略:在 lockSdf 之前获取 lockCal。看起来 "kind of" 解决了问题,但是
1) 这使得关于线程安全的推理变得过于困难
2) 它使 lockSdf 无用
假设 SDF 依赖于 CAL 并且 CAL 由 lockCal 保护,那么也可以使用 lockCal 保护 SDF。
Java 并发实践(Brian Goetz):
第 1 部分总结
...
Guard all variables in an invariant with the same lock.
...
如果 SDF
只被一个已经获取 lockCal
的线程使用,它一次只能被一个线程访问,即即使你删除它也是线程安全的lockSdf
.
上的锁
如果您选择依赖这个观察结果,您应该清楚地记录下来,这样未来的维护程序员就不会在 synchronized (lockCal)
.
之外开始使用 SDF
我在 class 中有两个共享可变对象,它们的线程安全策略已定义为 "thread safe."
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);
}
我还为此 class 设置了一个策略,即必须始终在 lockSdf
之前获取 lockCal
。然而,这也是如此 class:
- CAL 可以在没有 SDF 的情况下锁定和使用,在这种情况下 SDF 不会被锁定。
- 除非同时使用 CAL,否则方法中永远不会使用 SDF
因为 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);
}
一般来说,这里不保证线程安全。假设 SDF 应该由 lockSDF 保护,但它在不同的锁下被修改,如果另一个线程仅获取 lockSDF,它可能看不到 SDF 更改的结果。 但是您有一个策略:在 lockSdf 之前获取 lockCal。看起来 "kind of" 解决了问题,但是
1) 这使得关于线程安全的推理变得过于困难
2) 它使 lockSdf 无用
假设 SDF 依赖于 CAL 并且 CAL 由 lockCal 保护,那么也可以使用 lockCal 保护 SDF。
Java 并发实践(Brian Goetz):
第 1 部分总结
...
Guard all variables in an invariant with the same lock.
...
如果 SDF
只被一个已经获取 lockCal
的线程使用,它一次只能被一个线程访问,即即使你删除它也是线程安全的lockSdf
.
如果您选择依赖这个观察结果,您应该清楚地记录下来,这样未来的维护程序员就不会在 synchronized (lockCal)
.
SDF