2
public  class DCL {
    private static DCL staticDcl;

    private final DCL finalDcl;

    private DCL(){
        //other init operation
        
        
        //the action must be last.
        finalDcl = this;
    }

    public static DCL getDCL() {

        if (staticDcl == null) {
            synchronized(DCL.class) {
                if (staticDcl == null) {
                    staticDcl = new DCL();
                }
            }
        }
        return staticDcl.finalDcl;
    }
}

can the above code run without any error in multithreading environment??

I want to use the key word final to implements the dcl not volatitle.

xanxus
  • 21
  • 4
  • 1
    You made the common mistake of not storing the read value into a local variable. It’s possible that a thread evaluates `staticDcl == null` to `false`, because another thread wrote a non-`null` value to `staticDcl`, hence, skips the `synchronized` block and reads `null` from `staticDcl` in the `return` statement, as without synchronization, the two read operations are not ordered. – Holger Feb 01 '21 at 15:21

2 Answers2

1

You may run into problems, see Why is volatile used in double checked locking?

I suggest to not use this pattern, but rather the static holder pattern (SHP), which guarantees all the mechanics you try to reproduce. And with SHP you don't even have to think about volatile because the JVM handles it directly for you.

public class DCL {
    private DCL(){
        //other init operation
    }

    public static DCL getDCL() {
        return Holder.INSTANCE;
    }

    private static final class Holder {
        private static final DCL INSTANCE = new DCL();
    }
}

Also note that private final DCL finalDcl; is redundant because you can just return the instance directly, there's no need to create a new field which equals this.

The benefit of this pattern is that INSTANCE is instantiated only when it is first referenced, i.e. when getDCL() is called. This is due to the nature of lazy class loading.

Lino
  • 19,604
  • 6
  • 47
  • 65
  • 2
    Don’t confuse loading, linking, and initialization. The `DCL` instance will be created [when the class `Holder` is initialized](https://docs.oracle.com/javase/specs/jls/se15/html/jls-12.html#jls-12.4.1). Since this lazy initialization is the same for all classes, you don’t need the `Holder` class, but can place the `static final` field directly into the `DCL` class and the initialization will happen when the first call of `getDCL()` is made. Only when the class has other non-private static members that could trigger the initialization earlier than intended, you need a holder class. – Holger Feb 01 '21 at 15:28
  • sorry, I cannot agree with the point "You could have thread initialize the staticDcl field, and another thread do the same because they don't know that staticDcl was updated. " because synchronized can ensure avoid it. – xanxus Feb 02 '21 at 00:58
  • @Lino sorry, I cannot agree with the point "You could have thread initialize the staticDcl field, and another thread do the same because they don't know that staticDcl was updated. " because synchronized can ensure avoid it. – xanxus Feb 02 '21 at 06:21
  • @Holger can show you code? I think his demo is right! because Holder is static inner class. so Holder class will not initialized when DCL class initialized, it happend when Holder.INSTANCE. – xanxus Feb 02 '21 at 06:29
  • 2
    @xanxus the code of this answer is correct. But the explanation referring to “lazy loading” is not, as the field is initialized at the specified *class initialization time* (follow the link of my previous comment). You could run this code on a JVM with eager class loading and it still would work as intended. And once you followed my link and read the specification, you understood that you don’t need an inner class, as lazy initialization works for the `DCL` class as well. The example still is correct, but could be simplified. – Holger Feb 02 '21 at 09:52
1

Yes, without volatile, it is potentially broken, you can read my other answer for an explanation.

It is rather weird that you have staticDcl and finalDcl. If you want to provide singletons via that DCL, you do not need both. You can read the beginning of this question on how to achieve that. It also discusses how you could achieve double check locking safety without volatile, but still with some special semantics like release/acquire.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • yes. you are right. but I just want to know can we implements the dcl singleton with final. – xanxus Feb 02 '21 at 01:02
  • I will provide the global reference with staticDcl and avoid this point escape。 – xanxus Feb 02 '21 at 01:12
  • @xanxus you do know you can do `private static final DCL staticDcl = new DCL();` and have `public static DCL getDCL() { return staticDcl; }` and be done with it, right? you do _not_ need anything else. – Eugene Feb 02 '21 at 01:24
  • yes, i know it. it's a good idea. I offen use this method in my work. – xanxus Feb 02 '21 at 01:52
  • my purpose is not find a good method to implements the dcl singleton . just only want to know can we implements it with final and why or how? – xanxus Feb 02 '21 at 01:56
  • @xanxus what you are asking is vague, at best. `final` is about properly publishing an object, `DCL` is about singleton factories. You seem to confuse yourself too much here – Eugene Feb 03 '21 at 16:38