How to apply the Singleton pattern correctly?

Asked

Viewed 4,825 times

5

The class PEHandlerService need to have a single instance and do not want to use synchronized because of the slowness, I developed the following code:

public class PEHandlerService extends PlanilhaHandler {
    private LanguageHandler languageHandler;
    private static PEHandlerService instancia = new PEHandlerService();

    private PEHandlerService() {
        this.languageHandler = new LanguageHandler();
    }

    public static PEHandlerService getInstancia() {
        return instancia;
    }
}

I get java.lang.NullPointerException after I instant and access the methods, I tried to instantiate PEHandlerService in the private builder and the same occurs.

I can only do it when I change it to:

public class PEHandlerService extends PlanilhaHandler {
    private LanguageHandler languageHandler;
    private static PEHandlerService instancia;

    private PEHandlerService() {
        this.languageHandler = new LanguageHandler();
    }

    public static PEHandlerService getInstancia() {
        if (instancia == null) {
            instancia = new PEHandlerService();
        }
        return instancia;
    }
}

The two codes should not return instancia? Why does this only happen in code 2?

  • 1

    Although the most common is to see the second form, I’ve done the first way and it works too. In the first form the instance will be created when the class is initialized and in the second form the instance will be created on demand (in the first consumption of getInstance), and this difference in the time of initialization is that it must be confusing some code that we are not seeing in the question (consumer code or parent class code Planilhahandler). Try showing these codes as well as the full exception stack trace.

2 answers

6


Its first form is "more correct", that is, without testing in the method getInstance. The book Effective Java (Effective Java) has discussed this issue deeply for many years.

Let’s see some points below...

Singleton without any competition

The implementation of Singleton Pattern simpler is this way:

private static PEHandlerService instancia;
public static PEHandlerService getInstancia() {
    if (instancia == null) instancia = new PEHandlerService();
    return instancia;
}

As you already know, this version could generate two instances in a somewhat unusual scenario, that is, if two threads execute the getInstance at the same time on the first call to the method.

Singleton competitor

To solve this, the easiest solution is to synchronize the method:

private static PEHandlerService instancia;
synchronized public static PEHandlerService getInstancia() {
    if (instancia == null) instancia = new PEHandlerService();
    return instancia;
}

This avoids competition problems, but generates a small delay in each call to the method to manage the competition, in addition to multiple threads only one can call the method at a time, possibly generating bottlenecks in a highly concurrent system.

Singleton concurrent with minimal synchronization

To slightly improve the above version, some authors propose the following construction:

private volatile static PEHandlerService instancia;
public static PEHandlerService getInstancia() {
    if (instancia == null) {
        synchronized (PEHandlerService.class) {
            if (instancia == null) instancia = new PEHandlerService();
        }
    }
    return instancia;
}

This causes synchronization to occur only on startup and not on other calls.

However, note the modifier volatile in the class attribute. It is necessary even with synchronization, because due to the Java memory model, especially before Java 5, errors could still occur caused by a type of cache where others thread could still see the value null variable, even after assignment by another thread atomic mode.

Beware of multiple command initializations

A very important precaution is not to assign the object to the static variable before it is fully initialized. Consider the following code:

private volatile static PEHandlerService instancia;
public static PEHandlerService getInstancia() {
    if (instancia == null) {
        synchronized (PEHandlerService.class) {
            if (instancia == null) {
                instancia = new PEHandlerService();
                instancia.setAlgumaDependencia(new Dependencia());
            }
        }
    }
    return instancia;
}

The above code assigns a new instance of PEHandlerService à instancia and then passes some object to it. The problem is that as instancia != null, another thread can call the method getInstancia and retrieve the object before it receives the dependency. In this case you could have a NullPointerException.

Singleton preloaded without synchronization

To avoid all these problems above, the simplest solution pointed out is to simply initialize your object Singleton out of method getInstance, exactly as in your first example:

private static PEHandlerService instancia = new PEHandlerService();
public static PEHandlerService getInstancia() {
    return instancia;
}

If some kind of initialization is required, it is possible to use a static boot block:

private static PEHandlerService instancia;
static {
    instancia = new PEHandlerService();
    instancia.setDependencia(new Dependencia());
}
public static PEHandlerService getInstancia() {
    return instancia;
}

The biggest difference of this approach is that the object will no longer be initialized by demand (Lazy initialization), but as soon as the class is used for the first time (Eager initialization). This can be good or bad, depending on the case.

Singleton concurrent without synchronization

To try to put everything together, that is, avoid synchronization and load the Singleton on-mode Lazy, there are some alternatives.

One of them is to use a third class to load the static variable only when it is accessed. Example:

private static class SingletonLoader {
     private static PEHandlerService instancia = new PEHandlerService();
}
public static PEHandlerService getInstancia() {
    return SingletonLoader.instancia;
}

Alternative: use an Enum

Another alternative to Single is simply to declare your class as an Enum of one value. Example:

public enum PEHandlerServiceSingleton {
    INSTANCE;

    //métodos aqui

}

And then you can access it as follows:

PEHandlerServiceSingleton.INSTANCE

Considerations

There are many different ways to use a pattern like Singleton. Each one can be good or bad for certain situations and some hide certain problems.

However, once you understand the difference between implementations a little bit, it’s not hard to choose one that fits better in your solution.

  • 3

    Nice, the full explanation. A small correction in the text: static attributes are not initialized when the class is loaded nor during program startup, and static initializers are also not executed at this time. Instead, they are initialized during class initialization, which occurs, roughly speaking, in the first use of the class.

  • @Caffé Thanks for the tip.

1

Try to see if the error is not being caused elsewhere. Because your second code seems to be correct.

This is the correct way to implement a Singleton:

//Crie uma variável private para armazenar a instancia
private static PEHandlerService instance;
public static PEHandlerService GetInstance(){
    //Verifica de instance é null. Caso seja, instancia PEHandlerService.
    if (instance == null)
         instance = new PEHandlerService();
    return instance; // retorna a instancia
}

//o resto do código

Any doubt is just talk.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.