Double-checked blocking

from Wikipedia, the free encyclopedia

A double-checked locking ( English double-checked locking ) is a pattern in software development, which serves to access a shared object by a plurality of concurrent threads to regulate.

An incorrectly implemented double-checked lockout is an anti-pattern . This often happens to inexperienced programmers who know about the problem of locking but draw the wrong conclusions.

Double-checked blocking in Java

Although a double-checked blocking can be implemented thread-safe with Java 5 under a new semantics of the keyword , it is still considered an anti-pattern because it is too cumbersome and inefficient. In addition, the efficiency disadvantage of is hardly smaller than of . volatilevolatilesynchronized

example

The following example shows the problem in the getHelper()method, in which exactly one Helper object should be created for each Foo object the first time it is accessed:

public class Foo {
   private Helper helper = null;

   public Helper getHelper() {
     if(helper == null) // erste Prüfung
       synchronized(this) {
         if(helper == null) // zweite Prüfung
           helper = new Helper();
       }
     return helper;
   }

   // ...
}

The Helper interface is used to be able to work on the Helper object outside of a Foo object. If helper is not defined as volatile , as is the case here , the double check is problematic because e.g. For example, a Java JIT compiler can reorder the assembler code so that the reference to the Helper object is set before the constructor of the Helper object has been completely processed. In this case getHelper () returns an uninitialized object.

solution

Starting with Java 5, volatile defined variables are only visible after the constructor has been completely processed. If the variable helper is defined as volatile , the above example runs correctly.

If - as with the implementation of a singleton - there should only be a single instance per class, there is a solution that is easy to implement: The attribute is staticdeclared as and the creation of the object is separated into a subclass (here: nested class ) initialization on demand holder -Idiom .

public class Foo {
   private static class HelperHolder {
      public static Helper helper = new Helper();
   }

   public Helper getHelper() {
      return HelperHolder.helper;
   }

   // ...

The static attribute of the class is HelperHolder only getHelper()instantiated when it is called , ie “lazy”, and the virtual machine ensures thread safety.

Double-checked locking in C #

Similar to Java, the double-checked blocking also exists in C # .

class Singleton
{
    private Singleton() { }
    private static Singleton instance;

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock(_lock)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    // Hilfsfeld für eine sichere Threadsynchronisierung
    private static readonly object _lock = new object();
}

If no dedicated auxiliary field is to be used, as in Java, it can be thislocked. However, this is considered bad practice, as this can easily lead to so-called deadlocks. A dedicated lock object is preferable in almost every case.

class Singleton
{
    private Singleton() { }
    private static Singleton instance;

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (this)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }

            return instance;
        }
    }
}

Another possibility is to initialize the singleton variable directly in the field declaration. This means that the checked lock is superfluous, but the initialization is then no longer lazy or is carried out when the class is accessed for the first time. Thread safety is guaranteed by the CLR.

class Singleton
{
    private Singleton() { }
    private static readonly Singleton instance = new Singleton();

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

The best option as of .NET 4.0 is to use the Lazy<T>class, which internally uses a correct form of double-checked locking.

Web links

Further explanation of the topic with assembler example (English)

Individual evidence

  1. Java Language Specification, Java SE 7 Edition: 12.4.1
  2. Java Language Specification, Java SE 7 Edition: April 12, 2
  3. lock statement (C # reference). In: MSDN. Microsoft, accessed October 17, 2014 .
  4. Lazy <T> Class. In: MSDN. Microsoft, accessed July 27, 2014 .
  5. Ben Watson: Writing High-Performance .NET Code . 2014, ISBN 978-0-9905834-3-1 (English).