Thread safe

O que é “thread safe” ?

Eu procurei pra saber o q é, mas apenas achei conteudos falando q X é/não thread safe…

É o thread protegido de possíveis erros durante operações simultâneas.

Para um thread ser safe precisa usar a palavra chave synchronized no método que será usado na execução do thread.

Para mais informações, baixa a apostila da excelente escola: Caelum.

Tem um capítulo só de thread.

Thread-safe é um trecho de código mantém o seu estado válido, mesmo sendo percorrido por múltiplas linhas de execução.

Por exemplo,

Suponha a classe:

[code]public void ContaCorrente {
private float saldo;

public void saque(float quantia) {
float saldoResultante = saldo - quantia;
if (saldoResultante &lt 0)
throw new IllegalArgumentException(“Sem saldo”);
saldo = saldoResultante;
}
}[/code]

O método saque não é thread-safe. Por que?

Vamos supor que a conta corrente tenha 100 reais de saldo. Então, uma thread tenta fazer um saque de R$60,00 e outra de R$80,00.

A primeira thread executa a primeira linha, e calcula que os saldo resultante será de R$40,00. O processador então resolve trocar de thread, e passa a execução para a segunda thread.

A segunda thread executa a primeira linha e calcula o saldo resultante, que será de R$20,00, pois primeira thread ainda não atualizou o saldo. Ela continua a execução e atualiza o saldo para R$20,00.

O processador então volta para a primeira thread, que, a atualiza o saldo para R$40,00.

Como vc pode ver, nosso cliente foi beneficiado… ele sacou R$140,00 com uma conta com saldo de R$100,00 e ainda sobraram R$40,00.

Uma clara violação do estado da classe. Por isso, ela não é thread-safe.

O dificil em threads é que a ordem de processamento das threads é totalmente imprevisível. E nem mesmo trocar as prioridades das threads resolve o problema, pois a decisão final de que thread será executada é do sistema operacional. E a imprevisibilidade é cada vez maior, com os computadores com mais de um núcleo de processamento.

Por isso, usa-se mecanismos para garantir thread-safety. Sejam blocos sincronizados, variáveis diferentes para threads diferentes, filas de mensagens.

1 curtida

ViniGodoy bela explicação!
Vlw

Muito boa a explicação Vinicius !!!

Obrigado!!!

Caramba, tirou todas as dúvidas que eu também tinha.

Valeu Vinicius!!!

Sem dúvida explicação ótima !

Agora pra tirar 10 ! rs

Como transformar esse codigo thread-safe e evitar esse problema ?

[]s

Olha o vinigodoy sempre da boas explicações muita claras e curtas, e esta sempre a atento para ajudar os outros , obrigado pela ajuda.
e pensando nisso me veio a idea de todps os anos ter uma sessão de votos para o membro mais querido do GUJ , vou abrir um topico para todo o pessoal opinar sobre esta idea :idea: :idea:

No caso desse código, basta colocar a palavra “synchronized” no método.

public synchronized void saque(float quantia) { 

Olhando para este problema não consigo resistir: se a ContaCorrente for imutavel, como eu poderia fazer operações de saque entre diferentes threads? Seria o caso, imagino, de criar uma nova conta corrente com saldo atualizado… mas to com preguiça de pensar no resto e nas implicações…

O duro é que duas threads poderiam criar contas correntes diferentes a partir da mesma origem e, no caso, você teria o problema de novo. Como os dados de conta corrente serão persistidos, é difícil ter uma classe realmente imutável para esse caso.

Geralmente, as classes realmente imutáveis só representam valores simples. Seria o caso de uma classe chamada “Saldo”, por exemplo, contendo o saldo numa determinada data.

É… teria que sincronizar a operação de saque ou criar algum mecanismo de transação compensatória (se é que é possivel).

Fico pensando em como escalar um sistema desses sem cair em algo como 2-phase-commit da vida, pois os dados (ate aonde eu vejo) tem que estar sempre atualizados e consolidados a todo o instante (senão eu tenho 5 mil mas consigo sacar 10 de sacanagem). Q gargalo…

[quote=peczenyj]É… teria que sincronizar a operação de saque ou criar algum mecanismo de transação compensatória (se é que é possivel).

Fico pensando em como escalar um sistema desses sem cair em algo como 2-phase-commit da vida, pois os dados (ate aonde eu vejo) tem que estar sempre atualizados e consolidados a todo o instante (senão eu tenho 5 mil mas consigo sacar 10 de sacanagem). Q gargalo…[/quote]

Eu também pensei em algo assim. Ao invés de criar uma nova de conta, vc criaria uma nova transação dizendo que “o saldo foi deduzido em x”. E depois poderia aplicar as n transações, num momento distinto. Mas isso ainda deixa a possibilidade do cliente sacar além do limite.

Realmente, é complicado. Talvez não seja a toa que por isso os mainframes ainda estejam fazendo essa tarefa de maneira seriada, mas muito rápida.

Com o versionamento das informações (classes de domínio) o problema não seria resolvido??

Segundo a própria apostila da Caelum, uma solução poderia ser criar uma “trava” fazendo que quando uma determinada Thread entre em um desses métodos, ela fique trancada, sem permitir que nenhuma outra Thread acesse esses métodos.

A palavra chave é: synchronized.

[code]public class conta{
private double saldo;

 //outros métodos e atributos

 public void atualiza(double taxa){
      synchronized (this) {
           double saldoAtualizado = this.saldo * (1 + taxa); //o exemplo em questão cobra uma determinada taxa na conta
           this.saldo = saldoAtualizado;
      }
 }
 public void deposita(double valor){
      synchronized (this) {
           double novoSaldo = this.saldo + valor;
           this.saldo = novoSaldo;
      }
 }

}[/code]

[quote=Daniel Sousa]Segundo a própria apostila da Caelum, uma solução poderia ser criar uma “trava” fazendo que quando uma determinada Thread entre em um desses métodos, ela fique trancada, sem permitir que nenhuma outra Thread acesse esses métodos.

A palavra chave é: synchronized.

[/quote]Cuidado com essa afirmação. Apenas usar synchronized pode não resolver o problema (ela não é tua “bala de prata” :wink: ). Para isso funcionar a classe conta teria que ser a mesma utilizada pelas n Threads. Isso pq vc está declarando como monitor o objeto this (referência a uma instância de conta).

Caso existam 2 instâncias de conta para um mesmo cliente, isso não funcionaria.