Ultimamente estou dando uma "relida" no livro Head First Design Patterns (o qual eu recomendo para programadores Java pela didatica e exemplos de codigo).Passando pelo capitulo que fala sobre o design do Singleton, acabei criando uns testes legais no meu PC que mostram como e possivel cair em armadilhas achando que esta criando uma classe corretamente. O design do Singleton e muito simples e acredito que isso motiva muitos programadores a nao ir mais a fundo e acabam achando que sao mestres em Design Patterns (e vendem isso como facinho - isso que e desmerecer o proprio trabalho).Bom, a definicao formal do Singleton e a seguinte (conforme a definicao do GoF):"O Singleton garante que uma classe tenha apenas uma instancia e fornece um ponto global para acessar esta instancia". O problema em questao e o seguinte. Ao usar um singleton e possivel ter a ilusao de que sempre havera uma unica instancia de uma classe por toda eternidade enquanto o sistema estiver executando.Geralmente, a definicao geral para uma classe Singleton e a seguinte:public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}Escrever uma classe da forma acima nao garante que teremos uma unica instancia. Pensando no todo, devemos considerar esta classe sendo utilizada em um ambiente multi-thread.A maioria dos projetos em que se trabalha com Java, sao projetos para WEB, e como todos sabemos, para cada requisicao realizada no servidor, uma nova thread e criada. Portanto, com esta classe Singleton que escrevi acima, nao podemos afirmar que existira apenas uma instancia. Pode haver uma instancia para cada thread.Para simular esta situacao, criei uma classe muito parecida com a do livro do HF (Head First) e criei outra classe que cria threads para utilizacao da primeira.Abaixo segue o codigo da primeira classe (O Singleton que queremos utilizar no projeto).ChocolateBoiler.javapackage br.com.testes.patterns.singleton;public class ChocolateBoiler { private boolean empty; private boolean boiled; private static ChocolateBoiler instance; private ChocolateBoiler() { empty = true; boiled = false; System.out.println("criando instancia"); } public static ChocolateBoiler getInstance() { if (instance == null) { instance = new ChocolateBoiler(); } return instance; } public synchronized void fill() { if (isEmpty()) { System.out.println("filling the choco tank"); empty = false; boiled = false; } } public synchronized void drain() { if (!isEmpty() && isBoiled()) { System.out.println("draining the chocolate"); empty = true; } } public synchronized void boil() { if (!isEmpty() && !isBoiled()) { System.out.println("boiling the chocolate"); boiled = true; } } public boolean isEmpty() { return empty; } public boolean isBoiled() { return boiled; }}Agora a classe abaixo monta o cenario multi-threaded que queremos testar. ChocoHolic.javapackage br.com.testes.patterns.singleton;public class ChocoHolic { public static void main(String[] args) { Runnable task = new Runnable() { @Override public void run() { ChocolateBoiler boiler = ChocolateBoiler.getInstance(); boiler.fill(); try { Thread.sleep((long) (Math.random() * 3 * 1000)); } catch (InterruptedException e) { } boiler.boil(); boiler.drain(); } }; new Thread(task).start(); new Thread(task).start(); new Thread(task).start(); }}Ao executar a classe ChocoHolic acima, um dos resultados que tive foi o seguinte:criando instanciafilling the choco tankcriando instanciafilling