Programação Boas Práticas de Concorrente Exemplos de erros comuns da programação concorrente...
Transcript of Programação Boas Práticas de Concorrente Exemplos de erros comuns da programação concorrente...
Súmario
● Principais problemas da programação concorrente e os mecanismos Java para resolvê-los.○ Race condition(synchronized, Lock e Atomic);○ Visibilidade (volatile)
● Exemplos de erros comuns da programação concorrente● Exemplo de resolução de erro de concorrência no SIGRH.
Race Condition
É uma condição especial que pode ocorrer dentro de uma região critica( trecho de código executado por múltiplas threads) onde a sequência da execução das threads pode ocasionar inconsistência nos valores dos recursos alterados dentro da região crítica.
Race Condition
O código no método add() é um exemplo de uma região crítica. Como sabemos, o método não é executado de forma atomica pela JVM, ao invés é dividido em instruções, tais como ler o contador da memória dentro de um registrador, adicionar o valor para o registrador e por último escrever o registrador na memória. Agora imagine duas threads, A e B, executando add() na mesma instância de Counter.
Race Condition
A duas threads adicionaram os valores 3 e 2 para o contador, assim era de se esperar que o valor do contador após a execução das duas threads fosse 5. No entanto, como a execução das thread é intrelaçada, o resultado é diferente.
Como prevenir?
Para prevenir a race condition deve-se assegurar que a região crítica será executado por uma instrução atômica, ou seja, deve haver uma exclusão mútua na execução das threads dentro da região crítica. Isso implica que uma thread não executará a região crítica até a outra thread deixar a mesma. Uma estratégia eficáz para resolver o problema é o mecanismo de lock, que consiste de uma thread necessitar adquirir o lock para poder entrar na região crítica. Quando uma thread está com o lock, as outras têm que esperar a liberação do lock para que uma delas possa adquiri-lo e executar na região crítica.
Synchronized
Java prover um mecânismo de lock por meio da palavra-chave synchronized.
Existem duas formas de usar o mecânismo: sincronizando o método ou o bloco.
Synchronized
Blocos synchronized são reentrant. Isso significa que se a thread entra em um bloco e adquire o lock para o objeto monitor do bloco( no último caso o this), então essa thread pode entrar em qualquer outro bloco que tenho o mesmo objeto monitor.
Lock (java.util.concurrent.locks)
Java oferece um mecanismo mais sofisticado de lock. A interface Lock permite que o desenvolvedor implemente seu próprio mecânismo de lock mas também oferece algumas implementações.
Atomic Variable Classes
A estratégia de Locking tem algumas desvantagens, a principal é o baixo desempenho, o que é mais evidente em um ambiente com muitas threads, devido ao overhead de troca de contexto e tempo de espera das threads. Uma estratégia mais eficiente em termos de desempenho é a CAS(Compare and Swap), que é uma estrátegia que exige suporte do hardware. Essa estratégia é dita non-blocking.
Atomic Variable Classes
As Atomic Variables Classes em Java usam a estratégia CAS. AtomicInteger, AtomicLong, AtomicBoolean e AtomicReference dispõem de métodos que garantem a atomicidade das operações. Sendo assim, essas classes são thread-safe.
Visibilidade
Em um ambiente de multi-threads, muitas vezes uma thread não ver o valor atualizado de uma variável que está sendo compartilhada. Isso acontece porque, geralmente, cada thread mantém uma cópia local da váriavel, em cache, para melhorá o desempenho. Sendo assim, não é garantido que uma thread veja o valor atualizado, após a mudança na variável por uma outra thread.
Visibilidade
Para resolver esse problema a Java oferece o modificador volatile, que garante a visibilidade correta do valor da variável compartilhada. Usar esse modificador força a JVM a usar a memória principal(não o cache local).
Deve-se usar variáveis voláteis quando:•Escritas na variável não depende do seu próprio valor•A variável não participa do invariante com outras variáveis•Não é necessário bloquear (locking) a variável por qualquer outra razão
Visibilidade
Locking pode garantir ambas visibilidade e atomicidade, já as variáveis voláteis só podem garantir a visibilidade.
Thread safe
Uma classe é thread safe se ela está livre de race condition.
Obs: As classes Collections não são thread-safe, no entanto na própria classe Collections existem métodos que transformam coleções non-thread-safe em coleções thread-safe.
Referências
http://tutorials.jenkov.com/java-concurrency/race-conditions-and-critical-sections.html
http://tutorials.jenkov.com/java-concurrency/locks.html
http://www.vogella.com/tutorials/JavaConcurrency/article.html#concurrencyjava_synchronized
http://stackoverflow.com/questions/461896/what-is-the-most-frequent-concurrency-issue-youve-encountered-in-java
GOETZ, Java� Concurrency� In� Practice, Addison-Wesley Professional, 2006. Chapter 15. Atomic Variables and Non-blocking Synchronization.
https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html
https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html