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

时间:2015-08-06 04:44:05

标签: java concurrency synchronization thread-safety

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

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);
    }

2 个答案:

答案 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部分摘要

  

...

     

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

     

...