Thread-Safe Java: Entendendo o conceito e usando em sua aplicação

Veja neste artigo como utilizar o conceito de Thread-Safe na sua aplicação Java.

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.

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

Artigos relacionados