Pesquisar este blog

domingo, 5 de maio de 2013

Programacao concorrente synchronized

Programacao concorrente synchronized

Sincronizando seus metodos

Algumas vezes, quando estamos programando com threads, necessitamos que um determinado metodo seja acessado apenas por uma thread de cada vez, por exemplo, no banco de dados, os geradores de ids para tabelas, so podem ser acessados por apenas uma thread, senao poderiamos ter um resultado de ids repetidos para duas chamadas diferentes, o que geraria um erro em nosso banco.
Para fazer com que um metodo, ou mesmo uma parte do codigo, seja acessada por apenas uma thread, fazemos o uso da palavra chave synchronized, como mostrado abaixo:

public synchronized int getId(String nomeTabela){
       // codigo para retornar o proximo id de uma tabela
}
ou mesmo:

public int getId(String nomeTabela){
     System.out.println("Pegando proximo id da tabela" +nomeTabela);
     synchronized {
     // codigo para retornar o proximo id de uma tabela
     }
}
Como vimos existem duas maneiras de se usar a palavra synchronized, a diferenca entre elas e que, quando usada na assinatura do metodo, o metodo inteiro so podera ser acessado por apenas uma thread, ja da outra forma, apenas o codigo dentro demarcado pela palavra synchronized, tera o acesso limitado.
Bom, a primeira vista, isso parece uma grande maravilha, mas veja, a palavra synchronized mistura duas coisas antagonicas. Um estamos trabalhando com programacao concorrente, e agora estamos forcando a programacao concorrente voltar a ser executada por apenas uma thread, e isso e bem perigoso.
O grande perigo do uso da palavra synchronized sao os dead locks, mas o que e um dead lock?

Dead Lock

Um dead lock, ou tranca mortal, acontece quando temos duas threads em execucao, e as duas estao esperando o metodo que esta sendo executado acabar de executar, mas para que isso aconteca, o ouatro metodo devera terminar. No texto parece meio complicado de entender, entao vamos pra um exemplo de codigo de quando isso acontece:

public class Deadlock {
    static class Amigo {
        private final String nome;
        public Amigo(String nome) {
            this.nome = nome;
        }
        public String getNome() {
            return this.nome;
        }
        public synchronized void passar(Amigo passador) {
            System.out.format("%s: %s"
                + "  me passou a bola!%n", 
                this.nome, passador.getNome());
            passador.passaDeVolta(this);
        }
        public synchronized void passaDeVolta(Amigo passador) {
            System.out.format("%s: %s"
                + " voltou a bola pra mim!%n",
                this.nome, passador.getNome());
        }
    }

    public static void main(String[] args) {
        final Amigo neymar =
            new Amigo("Neymar");
        final Amigo ganso =
            new Amigo("Ganso");
        new Thread(new Runnable() {
            public void run() { neymar.passar(ganso); }
        }).start();
        new Thread(new Runnable() {
            public void run() { ganso.passar(neymar); }
        }).start();
    } 
}

Bom analisando o codigo acima, podemos ver que, muito provavelmente, quando executarmos esta classe, ela nunca ira terminar, e por que?
Uma das threads estara executando o metodo passaDeVolta, e ao terminar, ela deveria voltar a execucao do metodo passar, porem a outra thread estara parada no metodo passar, esperando o metodo passarDeVolta completar, e ai temos um lock mortal, ou seja, a thread 1 esperando a thread2 completar, e a thread2 para completar, precisa aguardar a thread1 completar.
Com o codigo, acontece exatamente o que esta acontecendo no cruzamento abaixo:
Aqui e bem facil achar o problema do codigo, agora em um sistema mais complexo isso nao e tao facil, pois as chamadas podem vir de varios lugares diferentes.
Usar thread, com synchronized nao e uma boa opcao em java, sempre que voce precisar usar o synchronized, e bem provavel que voce tenha problemas.


Nenhum comentário:

Postar um comentário