sábado, 15 de maio de 2021

Threads - Sincronização e outros cuidados com a execução paralela

 

Nesse artigo vou falar sobre sincronização de métodos para evitar problemas com o uso de threads. O uso de threads serve para agilizar a execução de rotinas, porém devemos estar sempre atentos como o que pode ou não ser acessado de modo paralelo por threads diferentes sem causar algum problema de consistência nos dados.

      

      Esse código abaixo mostra o acesso a um método de uma classe de negócio qualquer. Não temos como garantir qual thread vai acessar primeiro o método, e por mais que a thread1 acesse primeiro, não há nenhuma garantia que ela vai concluir todo o seu trabalho antes que a thread2 também acesse o mesmo método.


public class SincronizacaoThreads {

    public static void main( String[] args ) {

        ClasseDeNegocio classeDeNegocio = new ClasseDeNegocio();

        // Passando a tarefa e o nome do Thread

        Thread thread1 = new Thread( new Runnable() {

            @Override

            public void run() {

                // executa uma regra de negócio

                classeDeNegocio.metodoA();

            }

        }, "thread 1" );

 

        Thread thread2 = new Thread( new Runnable() {

            @Override

            public void run() {

                // executa uma regra de negócio

                classeDeNegocio.metodoA();

            }

        }, "thread 2" );

        thread1.start();

        thread2.start();

    } 

}

 

public class ClasseDeNegocio {

    public void metodoA() {

        String nome = Thread.currentThread().getName();

        System.out.println( nome + " entrando no metodoA" );

        try {

            Thread.sleep( 9000 );

        } catch ( InterruptedException e ) {

            e.printStackTrace();

        }

        System.out.println( nome + " terminando execução do metodoA" );

        System.out.println( nome + " saindo do metodoA" );

    }

} 

          Ao executar na minha máquina o resultado foi:

 







Por isso, temos que tomar alguns cuidados ao usar threads. Digamos que no metodoA chamado na classe de negócio houvesse acesso a dados para depois processar alguma coisa e gravar no banco, já imaginou o problema? As duas threads poderiam buscar os mesmos dados e fazer uma duplicação de informação, cálculos errados, etc.

 

O que poderíamos fazer para evitar esses problemas?

·     Sincronizar o acesso ao método

·    Consultar os dados antes e depois passar para cada thread os dados a serem trabalhados já divididos em dois conjuntos distintos.

 

Qual o melhor a se fazer? Depende de cada caso! Pode haver casos que seja necessário as duas soluções, outros apenas uma delas basta. Sempre que puder, evite a sincronização, pois ela cria um gargalo, porém, não deixe de usar onde necessário, pois traria grandes transtornos.


Veja como ficaria a sincronização do método:


public class ClasseDeNegocio {

    public void metodoA() {

        synchronized ( this ) {

            String nome = Thread.currentThread().getName();

            System.out.println( nome + " entrando no metodoA" );

            try {

                Thread.sleep( 9000 );

            } catch ( InterruptedException e ) {

                e.printStackTrace();

            }

            System.out.println( nome + " terminando execução do metodoA" );

            System.out.println( nome + " saindo do metodoA" );

        }

    } 

}


Como sincronizamos todo o método (pode haver casos que apenas um trecho precisa ser sincronizado) podemos usar o synchronized na declaração do método.  


public class ClasseDeNegocio {

    public synchronized void metodoA() {

        String nome = Thread.currentThread().getName();

        System.out.println( nome + " entrando no metodoA" );

        try {

            Thread.sleep( 9000 );

        } catch ( InterruptedException e ) {

            e.printStackTrace();

        }

        System.out.println( nome + " terminando execução do metodoA" );

        System.out.println( nome + " saindo do metodoA" );

    }

}


Veja como ficou o resultado da execução sincronizada:








O método passou a ser uma operação atômica, ou seja, a execução não pode ser interrompida na metade para que outra chamada entre nele. Espero que esse post tenha sido útil, até a próxima.

Nenhum comentário:

Postar um comentário