O que é Lazy Instantiation? [RESOLVIDO]

Eu estava lendo na net um pouco mais sobre Singleton (estou desenvolvendo uma aplicação JME e estou usufruindo do pattern Singleton para evitar excesso de utilização de memória) quando encontrei uma citação a Lazy Instantiation.

Fiquei curioso. Entretanto, ao pesquisar na net sobre uma definição para isso, não encontrei nada muito específico.

Alguém poderia explicar?

Desde já obrigado.


Dica de pcalcado:

Quer dizer que você só isntancia a classe quando precisa, não antes para poupar recursos. Costuma ter que ser feito com uma boa prática de sincronização, do contrário não adianta.

Agora vem cá, Singleton? Memória?

1 - Que tipo de objeto voce transformou em Singleton?
2 - Uma factory que retorna semrpe a mesma instancia nao ia resolver?

Cara, acredito que o que eu estou fazendo seja Singleton sim, e usa Lazy Instantiation também. Um exemplo:

public class Clients extends List implements CommandListener
  {
   private static Clients instance;
   ...

   private Clients()
     {
      ...
      }

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

      return (instance);
     }

Estou usando Singleton nos objetos que só necessitam de uma única instância, como os Forms, List’s, Canvas, etc, objetos de alto e baixo nível da interface gráfica.

Está incorreto?

P.S.: valew pelo help no LI.

Acho que é melhor você usar uma factory para isso. Pense num Singleton de um jeito como: “Se por uma caso eu tiver duas instâncias deste objeto por aceidente meu sistema vai estar em estado inválido”, para todas as outras coisas Fatories e Registries servem.

De qualquer forma, cuidado, seu código não é thread-safe.

Vale lembrar que só tem sentido falar em thread-safety se o código tiver mais de uma thread. Não é o caso de boa parte das aplicações JME.

Se for o caso de múltiplas threads, um singleton não thread-safe pode criar duas instâncias por acidente.

O Singleton no java é um problema também em aplicações com multiplos class loaders.

Que tal usar synchronized? Ele garante a segurança no caso de várias threads.

O código abaixo - minha autoria - é a implementação de um singleton, thread-safe e com lazy instantiation. Pode usar como inspiração se quiser.

public class ConnectionManager {
		
	private static ConnectionManager instance; 
	private Connection connection;
	private Properties properties;
	
	private ConnectionManager() {
				
		try {
			properties = new Properties();
			properties.load( ConnectionManager.class.getResourceAsStream("properties.txt"));
			
			//explicitly load JDBC driver
			Class.forName( properties.getProperty("driver") );
			
			//attempt to locate a suitable driver
			connection = DriverManager.getConnection( properties.getProperty("url"), 
					properties.getProperty("user"), properties.getProperty("password"));
			
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	/**
	 * @return an instance of the class
	 */
	//synchronization is expensive. Is this an issue?!
	public static synchronized ConnectionManager getInstance() {
		//lazy instantiation
		if ( instance == null ) {
			instance = new ConnectionManager();
		}
		
		return instance;		
	}
	
	/**
	 * @return the connection
	 */
	public Connection getConnection() {
		return connection;
	}
	
	/**
	 * @return the properties
	 */
	public Properties getProperties() {
		return properties;
	}
}

Verdade. Na verdade, nem tinha reparado que a pergunta era sobre Java ME, vou mover de categoria.

Isso é verdade. Mas cuidado com essa afirmação. Em vários casos (não é o caso acima) somente a palavra synchronized não garante nada. Na verdade, só tem sentido se preocupar com thread-safety se você tiver mais de uma thread.

Além disso, geralmente é uma boa idéia definir quais classes serão e quais não serão thread-safe e qual o grau de segurança elas possuem. Se possível, use técnicas obter thread-safe facilmente (como usar objetos diferentes em threads diferentes com o thread local).

Se não der para correr, prepare-se. Classes thread safe são difíceis de se implementar e ainda mais difíceis de se manter. Você tem que caprichar nos javadocs, principalmente se estiver num time.

O java é uma das linguagens que lida com isso da maneira mais inteligente que eu já vi. Mas ainda sim, não é tarefa das mais fáceis…

Mas Factories são usadas em casos onde necessita-se de várias instâncias de um objeto, não é? Melhor explicando… As Factories são usadas para gerenciar instâncias de objetos. Não faria sentido eu usar uma Factory para retornar sempre a mesma instância de um objeto se para isso eu tenho Singleton que é mais específico. Estou errado?

A pergunta não é sobre JME.

A pergunta era somente sobre o conceito da Lazy Instantiation. Entramos no assunto de JME devido ao seus comentários no primeiro post, pcalcado.

Quanto ao ViniGodoy, ele está certo quando fala em uma única thread. É uma aplicação que não usa várias threads. Outro fato é que estou usando Singleton em formulários e coisas simples “com o intuito único” de tentar usar o mínimo de memória possível. Os objetos com Singleton contêm apenas campos e comandos de tecla, não precisam de segurança e, também, nesta aplicação JME, “jamais” haverá motivos para eu ter 2 instâncias do mesmo formulário.

Essa questão de syncronized, etc, não faz muito sentido aqui uma vez que o objeto a ser aplicado o Singleton, no meu caso, não faz acesso a nenhum “meio externo”.

Acho, ainda, que este tópico deveria ser novamente movido ao “Java Básico” percebendo-se que ele trata de um conceito (a saber, Lazy Instantiation) e não JME.

Agradeço aos comentários de todos (e solicito, gentilmente, o bloqueio deste visto que o assunto sobressaiu-se ao título do tópico).

Att, Yky Mattshawn.

Ok, synchronized, Lazy Instantiation…
Perfeito.

Mas cuidado, principalmente no seu problema específico, lembre-se que uma vez que você instanciou seu singleton esse objeto não será mais destruído lembra??? Ou seja, mesmo em pontos onde você não o utiliza, ele estará sempre na memória após a primeira chamada.

Talvez hajam outras técnicas mais adequadas para isso.

Até.

[quote=nbluis]Ok, synchronized, Lazy Instantiation…
Perfeito.

Mas cuidado, principalmente no seu problema específico, lembre-se que uma vez que você instanciou seu singleton esse objeto não será mais destruído lembra??? Ou seja, mesmo em pontos onde você não o utiliza, ele estará sempre na memória após a primeira chamada.

Talvez hajam outras técnicas mais adequadas para isso.

Até.[/quote]

Realmente o que você faz sentido. A solução que me vem à cabeça é criar um método destroy que torna a variável instance nula. Claro que ainda tenho o problema de alguma classe não chamar o destroy, mas pelo menos assim estarei tornando a coisa menos pesada. Claro que ter de chamar o método para destruir o objeto é um tanto quanto elegível à classificação de “gambiarra”, mas seria uma alternativa.

Entretanto, pensando melhor agora, percebo que, uma vez que as instâncias de cada formulário serão usadas uma por vez (nunca exisitirá mais de uma instância do mesmo formulário), usar Singleton com instance e destroy é o mesmo que criar uma instância do objeto.

Antes de implementar o método destroy, acho bom você dar uma lida neste artigo que fala sobre o problema dessa abordagem..

O método destroy funcionaria no C++, mas não no java.

[quote=ViniGodoy]Antes de implementar o método destroy, acho bom você dar uma lida neste artigo que fala sobre o problema dessa abordagem..

O método destroy funcionaria no C++, mas não no java.[/quote]

Cara, é uma coisa óbvia, mas eu nunca havia pensado nisso, hehehe. Realmente muito interessante o artigo.

Entretanto, no meu caso ele não tem muita importância, pois como eu falei, minha aplicação nunca estará usando mais de uma instância do mesmo Form.

Inclusive, até agora não armazenei a instância em nenhum momento.

Uso sempre: Clients.instance().method();

E nunca: Clients c = Clients.instance();

Isso permite que eu chame o destroy() sem me preocupar com “referências” à instância (variável estática) em nenhum momento.

Uma vez que a tela apresenta sempre um único Form na tela, usar uma única instância do objeto parece-me o mais simples.

Toda aplicação Swing é multithreaded, mesmo que você não use threads. O mesmo para aplicações Java EE. Ser java ME importa e muito.

Ser Java ME faz com que economia de recursos ganhe de design bem feito, por isso Singletons podem fazer sentido 9apesar de um registry ser mais limpo).

Sincronização não tem a ver com meio externo, uma variável de instãncia mal sincronizada pode deixar seu objeto inválido.

Ser Java ME importa muito. Java ME é um subset de java, e sua programação é bem diferente.

Sim, está. Factories são classes que criam objetos, se eles criam um por requisição ou se sempre retornam o mesmo tanto faz.

Cara, andei pensando no problema de se limpar um singleton.

Existe uma forma de limpa-lo e ainda garantir que ele seja a única referência. Por isso, resolvi atualizar o meu artigo (aproveitei e já passei ele para o WordPress).

Dá uma olhada:
http://vinigodoy.wordpress.com/2007/05/10/cuidado-com-o-singleton/

FONTE: Java building

Inicialização e Lazy loading

Quando o singleton mapeia um recurso do SO ou até um recurso fisico ou periférico, é comum ser necessário vincular o objeto ao recurso através de algum processo de inicialização. Este processo em si mesmo pode ser demorado e consumir memória,processamento,ou requerer sincronismo entre threads diferentes. Por isso, é desejável que ele seja executado apenas quando necessário.

A inicialização pode acontecer no construtor, mas preferivelmente deveria acontecer em um método. Contudo, dado que a classe não pode ser extendida, não ha vantagem em usar um método para isto pois ele nunca será sobre-escrito. Isto não modifica o design anterior, pois basta colocar o codigo de inicialização dentro do construtor.

Para adiar o processo de inicialização é comum utilizar um processo conhecido como Lazy Loading e é comum ver implementações dele desta forma:

[code]public final class Desktop {
private static final Desktop ME;

public static Desktop getDesktop(){
    if ( ME == null ){
        ME = new Desktop();
    }
    return ME;
}
 
// Esconde o construtor.
private Desktop(){
    //inicialização
}
 
// métodos normais

} [/code]

Código 3: Implementação comum de singleton com lazy loading

Só que esta forma de implementação tem vários problemas [1]. Por exemplo, não é determinista em ambientes multi-thread podendo levar à criação de mais do que um objecto. Isso leva a considerar que o método getDesktop deveria ser sincronizado e optariamos por uma versão onde adicionariamos a palavra synchronized ao método getDesktop.

Mas, analizando melhor, o que realmente interessa sincronizar é a criação do objeto. Uma vez criado não teremos mais problemas com o sincronismo. Esta conclusão leva a inplementar o método desta forma:

[code]public final class Desktop {
private static final Desktop ME;

public static  Desktop getDesktop(){
    if ( ME == null ){
        synchronized(Desktop.class) {
          ME = new Desktop();
        }
    }
    return ME;
}
 
// o resto do codigo

} [/code]

Código 4: Implementação comum de singleton com lazy loading e sincronismo

Só que esta implementação, ainda não é suficiente para garantir sincronismo [2] o que leva as pessoas a criarem um anti-padrão desta forma:

[code]public final class Desktop {
private static final Desktop ME;

public static  Desktop getDesktop(){
    if ( ME == null ){
        synchronized(Desktop.class) {
            if ( ME == null ){
                ME = new Desktop();
            }
        }
    }
    return ME;
}
 
// o resto do codigo

} [/code]

Código 5: Implementação comum de singleton com lazy loading e sincronismo com dupla verificação

A dupla verificação não funciona, embora pareça que sim, e constitui um anti-padrão grave. A solução real para este problema passa pelo uso da palavra reservada volatile.

[code]public final class Desktop {

private static final volatile Desktop ME;
 
public static  Desktop getDesktop(){
    if ( ME == null ){
        synchronized(Desktop.class) {
            if ( ME == null ){
                ME = new Desktop();
            }
        }
    }
    return ME;
}
 
// o resto do codigo

}
[/code]

Código 6: Implementação comum de singleton com lazy loading e sincronismo com dupla verificação

Só que também esta solução era problemática proque nem todas as JVM implementavam o controle associado a volatile da mesma forma. Apenas após a versão 5 do Java, quando foi especificado o comportamento esperado associado a esta palavra é que se tornou seguro usá-la. Portanto, para código escrito em java 1.4 ou mais antigo, a solução é tornar todo o método sincronizado , para a versão 5 ou superior é utilizar a palavra volatile. Para singletons lógicos , em java 5 ou superior é melhor utilizar enum. Singletons lógicos são raros e provávelmente sempre os pode substituir pelo uso de outro padrão como Registry