繁体   English   中英

是否需要嵌套此代码所需的锁?

[英]Is nesting of locks necessary for this code?

我在类中有两个共享的可变对象,其线程安全策略已被定义为“线程安全”。

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,在这种情况下,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仅被已获取lockCal的线程使用,则它一次只能由一个线程访问,即使您在lockSdf上移除锁定它也是线程安全的。

如果您选择依赖此观察,则应该清楚地记录,以便将来的维护程序员不会在synchronized (lockCal)之外开始使用SDF

一般来说,这里不保证线程安全。 假设SDF应该被lockSDF保护,但是它在不同的锁下被修改,如果它只获取了lockSDF,则另一个线程可能看不到SDF更改的结果。 但是你有一个策略:在lockSdf之前获取lockCal。 看起来它“有点”解决了这个问题,但是

1)它对线程安全的推理太难了

2)它使lockSdf无用

假设SDF依赖于CAL并且CAL由lockCal保护,那么使用lockCal来保护SDF也是有意义的。

Java Concurrency in Practice(Brian Goetz):

第1部分摘要

...

使用相同的锁保护不变量中的所有变量。

...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM