![Singleton](/images/patterns/cards/singleton-mini.png?id=914e1565dfdf15f240e766163bd303ec)
Singleton em Java
O Singleton é um padrão de projeto criacional, que garante que apenas um objeto desse tipo exista e forneça um único ponto de acesso a ele para qualquer outro código.
O Singleton tem quase os mesmos prós e contras que as variáveis globais. Embora sejam super úteis, eles quebram a modularidade do seu código.
Você pode usar classes que dependem de singletons em algumas outras situações. Você terá que levar a classe singleton também. Na maioria das vezes, essa limitação surge durante a criação de testes de unidade.
Complexidade:
Popularidade:
Exemplos de uso: Muitos desenvolvedores consideram o padrão Singleton um antipadrão. É por isso que seu uso está diminuindo no código Java.
Apesar disso, existem muitos exemplos de Singleton nas bibliotecas principais do Java:
Identificação: O Singleton pode ser reconhecido por um método de criação estático, que retorna o mesmo objeto em cache.
Singleton ingênuo (thread única)
É muito fácil implementar um Singleton desleixado. Você só precisa ocultar o construtor e implementar um método de criação estático.
Singleton.java: Singleton
package refactoring_guru.singleton.example.non_thread_safe;
public final class Singleton {
private static Singleton instance;
public String value;
private Singleton(String value) {
// The following code emulates slow initialization.
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
this.value = value;
}
public static Singleton getInstance(String value) {
if (instance == null) {
instance = new Singleton(value);
}
return instance;
}
}
DemoSingleThread.java: Código cliente
package refactoring_guru.singleton.example.non_thread_safe;
public class DemoSingleThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
"If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
"RESULT:" + "\n");
Singleton singleton = Singleton.getInstance("FOO");
Singleton anotherSingleton = Singleton.getInstance("BAR");
System.out.println(singleton.value);
System.out.println(anotherSingleton.value);
}
}
OutputDemoSingleThread.txt: Resultados da execução
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
FOO
FOO
Singleton ingênuo (multithreaded)
A mesma classe se comporta incorretamente em um ambiente multithread. Vários threads podem chamar o método de criação simultaneamente e obter várias instâncias da classe Singleton.
Singleton.java: Singleton
package refactoring_guru.singleton.example.non_thread_safe;
public final class Singleton {
private static Singleton instance;
public String value;
private Singleton(String value) {
// The following code emulates slow initialization.
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
this.value = value;
}
public static Singleton getInstance(String value) {
if (instance == null) {
instance = new Singleton(value);
}
return instance;
}
}
DemoMultiThread.java: Client code
package refactoring_guru.singleton.example.non_thread_safe;
public class DemoMultiThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
"If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
"RESULT:" + "\n");
Thread threadFoo = new Thread(new ThreadFoo());
Thread threadBar = new Thread(new ThreadBar());
threadFoo.start();
threadBar.start();
}
static class ThreadFoo implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("FOO");
System.out.println(singleton.value);
}
}
static class ThreadBar implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("BAR");
System.out.println(singleton.value);
}
}
}
OutputDemoMultiThread.txt: Resultados da execuçãos
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
FOO
BAR
Singleton seguro para threads com carregamento lento
Para corrigir o problema, você deve sincronizar os threads durante a primeira criação do objeto Singleton.
Singleton.java: Singleton
package refactoring_guru.singleton.example.thread_safe;
public final class Singleton {
// The field must be declared volatile so that double check lock would work
// correctly.
private static volatile Singleton instance;
public String value;
private Singleton(String value) {
this.value = value;
}
public static Singleton getInstance(String value) {
// The approach taken here is called double-checked locking (DCL). It
// exists to prevent race condition between multiple threads that may
// attempt to get singleton instance at the same time, creating separate
// instances as a result.
//
// It may seem that having the `result` variable here is completely
// pointless. There is, however, a very important caveat when
// implementing double-checked locking in Java, which is solved by
// introducing this local variable.
//
// You can read more info DCL issues in Java here:
// https://refactoring.guru/java-dcl-issue
Singleton result = instance;
if (result != null) {
return result;
}
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton(value);
}
return instance;
}
}
}
DemoMultiThread.java: Client code
package refactoring_guru.singleton.example.thread_safe;
public class DemoMultiThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
"If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
"RESULT:" + "\n");
Thread threadFoo = new Thread(new ThreadFoo());
Thread threadBar = new Thread(new ThreadBar());
threadFoo.start();
threadBar.start();
}
static class ThreadFoo implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("FOO");
System.out.println(singleton.value);
}
}
static class ThreadBar implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("BAR");
System.out.println(singleton.value);
}
}
}
OutputDemoMultiThread.txt: Resultados da execuçãos
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
BAR
BAR
Quer mais?
Existem ainda mais sabores especiais do padrão Singleton em Java. Dê uma olhada neste artigo para descobrir mais:
Práticas Recomendadas para o Padrão de Projeto Singleton em Java com Exemplos