Um requisito muito importante antes de começar a trabalhar com Threads, é conhecer e saber utilizar estes como Thread-Safe, garantindo assim uma aplicação robusta e sem inconsistências.

O conceito de Thread-Safe surgi quando há a necessidade de trabalhar-se com programação concorrente, seu principal objetivo é garantir que 2 ou mais threads que estejam em “condição de corrida” não obtenham informações erradas (condição de corrida ou race condition ocorre quando várias threads desejam acessar o mesmo recurso).

A solução para evitar o race condition, quando houver necessidade, é utilizar a exclusão mútua, ou seja, certificar-se que para aquele determinado recurso apenas 1 thread poderá utilizá-lo por vez.

Imagine que temos o Recurso A, e que a thread 1 e 2 desejam acessar esse recurso de forma concorrente. Garantindo a exclusão mútua ao Recurso A, se a Thread 1 começar o acesso ao Recurso A, ela só perderá o acesso ao mesmo quando terminar toda a tarefa que ela deseja, enquanto isso, a Thread 2 fica esperando.

Este conceito descrito acima (race condition e exclusão mútua é essencial para entender o conceito de Thread-Safe), então vamos a um exemplo mais prático:

Imagine que há um Sistema que possui dois procedimentos concorrentes (vamos chamá-lo de procedimento A e procedimento B), onde ambos fazem uso de um Arquivo chamado “registroDeVariavel.txt” e sempre antes de usá-lo ele é limpo pelo procedimento que vai escrever nele o que desejar. Esse arquivo serve para escrita e leitura de variáveis por apenas um procedimento, por isso sempre que um novo procedimento vai utilizá-lo, ele deve ser limpo para evitar inconsistências.

  • O procedimento A acessa o arquivo e começa a realizar uma escrita no mesmo, porém no meio do processo o escalonador do processador interrompe sua execução e da prioridade ao procedimento B.
  • Neste momento o procedimento B limpa o arquivo e começa a escrever o que ele precisa e termina toda sua execução.
  • Após o término da escrita no arquivo pelo procedimento B, o procedimento A volta a ser executado e continua sua escrita normalmente, mas perceba aqui uma coisa super importante: O procedimento B apagou tudo que o A fez, então quando o A continuar a escrita o arquivo apresentará inconsistências.

Qual a solução para isso ? Usar Thread-Safe. Em Java podemos garantir a exclusão mútua através da palavra reservada “synchronized”, assim o procedimento B não poderá ser executado até o término do procedimento A.

Exemplo Prático com Thread-Safe

Na listagem 1 você pode ver um procedimento e java implementado com exclusão mútua, garantindo assim o comportamento Thread-Safe em nossa aplicação.

Listagem 1: Usando Synchronized


private synchronized void processarArquivo(String arquivo) {
			//faça aqui seu procedimento de leitura e escrita do arquivo
	}
	

Não é trivial desenvolver aplicações que usam a programação concorrente, isso porque todo o conceito de semáforo, exclusão mútua e condição de corrida estão envolvidos com threads. Este tipo de tarefa exige muita dedicação e análise, visto que um erro pode “bagunçar” toda a lógica da aplicação, e o pior é que descobrir onde está o erro é difícil, pois este é um erro semântico.

Thread-Safe, Single-Thread e Multi-Thread

Muita das vezes há confusão entre esses 3 conceitos, como já explicamos os tópicos anteriores o Thread-Safe garanti que não haverá conflito entre Threads concorrentes, ou seja, garante a segurança de aplicações que são Multi-Thread.

Uma aplicação Multi-Thread possui diversas Threads, ou seja, a programação concorrente se aplica aqui, várias tarefas simultaneamente, por outro lado o Single-Thread é uma aplicação que executa apenas 1 thread por vez. Atualmente é difícil, se não raro, ver uma aplicação que funcione apenas com Single-Thread, isso causaria gargalo em muitas aplicações que já funcionam com Multi-Thread.

Uma pergunta que muitos podem fazer é a respeito do “synchronized” do java: Se você usar esta palavra reservada está garantindo que apenas 1 thread por vez executará o seu processo, sendo assim sua aplicação não se tornaria Single-Thread ?

A resposta é não, o conceito de exclusão mútua não se adequa à aplicações Single-Thread. Note que Single-Thread é quando você tem apenas 1 Thread por vez sendo executada no processador, e nada mais. Mesmo que você use o “synchronized” ainda terá 2 ou mais tarefas no escalonador do processador, apenas aguardando seu momento para executar essa procedimento, além disso enquanto ela não executa a “região crítica” pois outro processo esta executando, ela pode fazer uma outra tarefa, então o conceito de Multi-Thread continua “vivo”.

Para assimilar mais ainda o conceito de Thread-Safe vamos ver uma classe correspondente ao padrão RGB implementando o Thread-Safe em blocos, ou seja, em vez de um método inteiro, podemos também utilizar em pedaços menores de código.

Listagem 2: Thread-Safe em blocos de código

public class RGBColor {

    private int r;
    private int g;
    private int b;

    public RGBColor(int r, int g, int b) {

        checkRGBVals(r, g, b);

        this.r = r;
        this.g = g;
        this.b = b;
    }

    public void setColor(int r, int g, int b) {

        checkRGBVals(r, g, b);

        synchronized (this) {

            this.r = r;
            this.g = g;
            this.b = b;
        }
    }

    /**
    * returns color in an array of three ints: R, G, and B
    */
    public int[] getColor() {

        int[] retVal = new int[3];

        synchronized (this) {

            retVal[0] = r;
            retVal[1] = g;
            retVal[2] = b;
        }

        return retVal;
    }

    public synchronized void invert() {

        r = 255 - r;
        g = 255 - g;
        b = 255 - b;
    }

    private static void checkRGBVals(int r, int g, int b) {

        if (r < 0 || r > 255 || g < 0 || g > 255 ||
            b < 0 || b > 255) {

            throw new IllegalArgumentException();
        }
    }
}

CONCLUSÃO

Tenha em mente que o conceito de Thread-Safe é muito mais do que apenas a palavra reservada “synchronized” em Java, pois muitos acham que se resumo a isso. Este conceito serve não só para Java mas para outras linguagens que implementam programação concorrente, ou seja, possuem o recurso apropriado para criar um ambiente multi-thread.

Espero que tenham gostado do artigo, se tiverem alguma dúvida podem ficar a vontade em postar nos comentários que irei ter prazer em responder. Até a próxima