Segurança de Tópicos em Classes Singleton Java

Singleton é um dos padrões de design de criação mais amplamente utilizados para restringir o objeto criado por aplicações. Se estiver sendo utilizado em um ambiente multi-threaded, a segurança de thread da classe singleton é muito importante. Em aplicações do mundo real, recursos como conexões de banco de dados ou Sistemas de Informação Empresarial (EIS) são limitados e devem ser utilizados com sabedoria para evitar qualquer escassez de recursos. Para alcançar isso, podemos implementar um padrão de design Singleton. Podemos criar uma classe de envoltório para o recurso e limitar o número de objetos criados em tempo de execução para um.

Singleton Thread Safe em Java

Em geral, seguimos as etapas abaixo para criar uma classe singleton:

  1. Crie um construtor privado para evitar a criação de novos objetos com o operador new.
  2. Declare uma instância estática privada da mesma classe.
  3. Fornecer um método público estático que retornará a variável de instância da classe singleton. Se a variável não estiver inicializada, inicialize-a; caso contrário, retorne simplesmente a variável de instância.

Usando as etapas acima, criei uma classe singleton que parece abaixo. ASingleton.java

package com.journaldev.designpatterns;

public class ASingleton {

	private static ASingleton instance = null;

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		if (instance == null) {
			instance = new ASingleton();
		}
		return instance;
	}

}

No código acima, o método getInstance() não é seguro para threads. Múltiplas threads podem acessá-lo ao mesmo tempo. Para as primeiras threads quando a variável de instância não está inicializada, múltiplas threads podem entrar no loop if e criar múltiplas instâncias. Isso quebrará nossa implementação singleton.

Como alcançar segurança de thread em uma Classe Singleton?

Existem três maneiras pelas quais podemos alcançar segurança de thread.

  1. Crie a variável de instância no momento do carregamento da classe.
    Prós :
  • Segurança de thread sem sincronização
  • Fácil de implementar

Contras :

  • Criação precoce de recurso que pode não ser usado na aplicação.
  • A aplicação cliente não pode passar nenhum argumento, então não podemos reutilizá-lo. Por exemplo, tendo uma classe singleton genérica para conexão de banco de dados onde a aplicação cliente fornece as propriedades do servidor de banco de dados.
  1. Sincronize o método getInstance().
    Prós :
  • A segurança de segmento é garantida.
  • A aplicação do cliente pode passar parâmetros
  • Inicialização preguiçosa alcançada

Contras:

  • Desempenho lento devido ao overhead de bloqueio.
  • Sincronização desnecessária que não é necessária uma vez que a variável de instância é inicializada.
  1. Use bloco sincronizado dentro do loop if e variável volátil
    Prós:
  • A segurança de segmento é garantida
  • A aplicação do cliente pode passar argumentos
  • Inicialização preguiçosa alcançada
  • O overhead de sincronização é mínimo e aplicável apenas para os primeiros threads quando a variável é nula.

Contras:

  • Condição extra se

Olhando para todas as três maneiras de alcançar a segurança de segmento, acredito que a terceira é a melhor opção. Nesse caso, a classe modificada ficará assim:

package com.journaldev.designpatterns;

public class ASingleton {

	private static volatile ASingleton instance;
	private static Object mutex = new Object();

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		ASingleton result = instance;
		if (result == null) {
			synchronized (mutex) {
				result = instance;
				if (result == null)
					instance = result = new ASingleton();
			}
		}
		return result;
	}

}

A variável local result parece desnecessária. Mas está lá para melhorar o desempenho do nosso código. Nos casos em que a instância já está inicializada (na maioria das vezes), o campo volátil é acessado apenas uma vez (devido a “return result;” em vez de “return instance;”). Isso pode melhorar o desempenho do método em até 25 por cento. Se você achar que existem maneiras melhores de alcançar isso ou se a segurança de segmento estiver comprometida na implementação acima, por favor, comente e compartilhe conosco.

Dica Extra

String não é uma escolha muito boa para ser usada com a palavra-chave synchronized. Isso ocorre porque elas são armazenadas em um pool de strings e não queremos travar uma string que pode estar sendo usada por outro trecho de código. Então estou usando uma variável de tipo Object. Saiba mais sobre sincronização e segurança de thread em Java.

Você pode conferir mais exemplos de Java em nosso Repositório no GitHub.

Source:
https://www.digitalocean.com/community/tutorials/thread-safety-in-java-singleton-classes