Segurança de Thread em Classes Singleton Java

Singleton é um dos padrões de design criacional mais amplamente utilizados para restringir o objeto criado pelas aplicações. Se você estiver usando-o em um ambiente multi-threaded, então 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 invólucro para o recurso e limitar o número de objetos criados em tempo de execução para um.

Singleton Thread Safe em Java

Geralmente, seguimos as etapas abaixo para criar uma classe singleton:

  1. Crie o 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. Forneça 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, simplesmente retorne a variável de instância.

Usando os passos 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 de singleton.

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

Há 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 antecipada 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, ter uma classe singleton genérica para conexão com banco de dados onde a aplicação cliente fornece propriedades do servidor de banco de dados.
  1. Sincronize o método getInstance().
    Prós:
  • A segurança da thread é 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 é requerida uma vez que a variável de instância é inicializada.
  1. Usar bloco sincronizado dentro do loop if e variável volátil
    Prós:
  • A segurança da thread é 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 as primeiras threads quando a variável é nula.

Contras:

  • Condição extra do if

Olhando todas as três maneiras de alcançar a segurança da thread, eu acredito que a terceira é a melhor opção. Nesse caso, a classe modificada parecerá 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 resultado parece desnecessária. Mas, está lá para melhorar o desempenho do nosso código. Em casos onde 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 melhores maneiras de alcançar isso ou se a segurança da thread estiver comprometida na implementação acima, por favor comente e compartilhe conosco.

Dica de Bônus

String não é uma candidata 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 do tipo Object. Saiba mais sobre sincronização e segurança de threads em Java.

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

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