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
由于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。看起来它“某种程度上”解决了问题,但是

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