How to apply the Singleton pattern correctly?

5

The class PEHandlerService needs to have a single instance and I do not want to use synchronized because of the slowness, I created 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 instantiate and access the methods, tried to instantiate PEHandlerService in the private constructor, and the same occurs.

I can only do this when I change 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;
    }
}

Should not the two codes return instancia ? Why does this only occur in code 2?

    
asked by anonymous 27.03.2015 / 13:19

2 answers

5

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

Let's see some points below ...

Singleton without competition

Simpler implementation of the singleton pattern is like this:

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 executed getInstance at the same time in the first method call.

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 issues, but generates a short delay in each method call to manage the competition, and if there are 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 improve the above version a little, 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 volatile modifier in the class attribute. It is necessary even with synchronization, because due to the Java memory model, especially before Java 5, there could still be errors caused by a cache type where other thread you could still see the null value in the variable, even after the assignment by another thread atomic mode.

Beware of multiple command initializations

A very important care is to not 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 to instancia and then passes some object to it. The problem is that as instancia != null , another thread can call the getInstancia method and retrieve the object before it receives the dependency. In this case you could have a NullPointerException .

Singleton preloaded without synchronization

To avoid all of these problems, the simplest solution is simply to initialize your singleton object outside the getInstance method, just as in your first example:

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

If some type of boot is required, you can 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 on demand ( lazy initialization ), but as soon as the class is first used eager initialization ). This can be good or bad depending on the case.

Singleton concurrent without synchronization

To try to merge everything, that is, to prevent synchronization and load singleton in lazy mode, 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 declaring your class as an Enum of a single 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 can be good or bad for certain situations and some hide certain problems.

However, once you understand the difference between deployments, it's not hard to choose the one that fits your solution better.

    
27.03.2015 / 17:22
0

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 questions just talk.

    
27.03.2015 / 13:35