Final keyword vs synchronized

6 respostas
P

Oi,

tenho uma dúvida em relação à keyword “final” e a criação de um objecto dentro de um synchronized. É óbvio que a classe onde ponho o código abaixo é acedido por várias threads, e não pode haver mais que uma instância do mesmo objecto.

Por exemplo:

ter:

public static final Myclass mc = new MyClass();

ou ter

private static Myclass mc = null;

public synchronized static getInstance()
{
  if ( mc == null )
    return mc = new Myclass();
 return mc;

}

é a mesma coisa? Se sim, porquê?

Obrigado

6 Respostas

sergiotaborda

Não, não é a mesma coisa. No primeiro vc não tem qq concorrência.

P

Mas é possível alguma vez ter duas instâncias do Myclass, no caso em que existe várias threads a executarem a instrução concorrentemente, independentemente da ordem de execução?

public static final Myclass mc = new MyClass();

Eu até arrisco a dizer que:

public final Myclass mc = new MyClass();

também está protegido contra a concorrência.

É verdade?

Obrigado.

XisPe
public static final Myclass mc = new MyClass();

Nao eh possivel ter duas instancias do MyClass, uma vez que o metodo é estático e a referência ao seu objeto MyClass ficará na classe. TODAVIA, há a possibilidade de criação de dois ou mais objetos devido ao acesso concorrente (apenas um deles será referenciado). Note que apesar de pouco provável, pode ser que um ou mais objetos sejam criados e a classe guarde referência para apenas um deles.

Mais uma vez, não há garantia contra concorrência. Lembre-se que a concorrência é evitada com a palavra syncronized ou através de operações atômicas (não sei quais são todas). A única diferença deste para o anterior, é que a referência para os objetos MyClass ficarão nos objetos, logo, há a possibilidade de criação de mais de um objeto MyClass e de sua preservação através de referências.

ViniGodoy

Não, esse possibilidade não existe.
A carga da classe pelo class loader (desde que exista apenas 1 class loader), é garantida e é Thread-safe. Isso inclui a inicialização dos atributos static e final. Além disso, as threads não precisam fazer qualquer tipo de cache de atributos final e, mesmo que façam, referenciarão o mesmo valor de atributo, pois sua inicialização é garantida (caso contrário, um final poderia ter 2 valores, um antes e outro depois da inicialização).

Só tome cuidado que final não é substituto para a palavra synchronized. O que garante Thread-Safety nesse caso é o mecanismo de class loading, a presença de um único class loader, e as palavras static final. O final por si só, não é garantia de nada.

eduveks

Só um conselho…

Não use muito o synchronized, apenas quando é realmente necessário, se não tu vai acabar causando problemas de concorrência e sincronização, travando as threads…

Foi um problema que já tive, e a solução foi tirar os diversos synchronized, q estavam sendo usados sem necessidade, problema resolvido.

Quem souber mais detalhes sobre esta caracteristica era bom q ajudasse a exclarecer.

ViniGodoy

eduveks:
Só um conselho…

Não use muito o synchronized, apenas quando é realmente necessário, se não tu vai acabar causando problemas de concorrência e sincronização, travando as threads…

Foi um problema que já tive, e a solução foi tirar os diversos synchronized, q estavam sendo usados sem necessidade, problema resolvido.

Quem souber mais detalhes sobre esta caracteristica era bom q ajudasse a exclarecer.

Na verdade, o problema estava na sua aplicação e não nos blocos synchronized. É bem provável que você estivesse usando os blocos da maneira errada. A maior parte dos erros, para quem começa a mexer com threads, é usar synchronized demais sobre a mesma chave de sincronização (provavelmente this, que é a chave default).

Lembrando: Se um de seus blocos synchronized bloqueia a chave, nenhum outro bloco poderá ser acessado.

Muitas vezes, usar 2 chaves de sincronização resolveria o problema. Um caso de erro típico é deixar um bloco vigiando um InputStream de um socket e outro enviando dados pelo OutputStream. Enquanto o método read está travado no InputStream, o write não pode ser usado no OutputStream. E isso é um problema bem grande se o read só for desbloqueado em resposta a um comando do write…

Quando você removeu o synchronized, você realmente eliminou um problema: o deadlock. Entretanto, certamente criou outro problema, o de acesso concorrente. Sua aplicação pode ter poucas chances de cair em situações de concorrência que te causam dor de cabeça, e por isso a sensação do problema resolvido. Entretanto, se um belo dia o problema ocorrer, vai ser difícil de depurar e difícil de reproduzir. E vai ocorrer esporadicamente.

Uma das coisas ruins, na minha opinião, é que vende-se a idéia que lidar com múltiplas threads é um assunto trivial em Java. Embora a linguagem forneça mecanismos nativos e muito bons, e certamente seja mais fácil lidar do que no C++, lidar com múltiplas threads ainda é um assunto complicado e cheio de detalhes. O ideal é recorrer a API do java.util.concurrent sempre que possível e fazer threading no braço só quando não tiver outra maneira de resolver o problema.

Criado 28 de agosto de 2007
Ultima resposta 29 de ago. de 2007
Respostas 6
Participantes 5