You are on page 1of 55

Tpicos

selecionados
de
programao
em

Java

Threads em Java
Padres, anti-padres
e boas prticas

Helder da Rocha
Julho 2005

1. Entenda o significado de
synchronized

(PJ)

Para entender corretamente synchronized preciso


entender o modelo de memria
Java possui um modelo de memria que descreve o
comportamento de aplicaes concorrentes e paralelas em
relao memria compartilhada
Nesse modelo, cada CPU (real ou virtual) tem uma cpia dos
dados compartilhados em cache
No h garantia que os dados em cache estejam em dia com os
dados compartilhados a no ser que estejam em um bloco
synchronized, ou sejam variveis volteis

Portanto
possvel acessar um objeto compartilhado fora de um bloco
synchronized, mesmo que outro mtodo tenha sua trava, mas
no h garantia que o estado observado seja correto nem que
quaisquer mudanas sero preservadas
2

Diagrama: modelo de memria


Funcionamento de synchronized
1.
2.
3.
4.
5.

synchronized (objeto)
{
Obtm trava
Atualiza cache local com dados da memria compartilhada
Manipula dados localmente (interior do bloco)
}
Persiste dados locais na memria compartilhada
Libera trava

Fonte: [LEA1]

2. Sincronize o acesso a dados


mutveis e compartilhados (EJ 48)
Dados compartilhados nunca devem ser
observados em um estado inconsistente
importante que as mudanas ocorram de um
estado consistente para outro

Existem apenas duas maneiras de garantir que


mudanas em dados compartilhados sejam
vistos por todos os threads que os utilizam
Realizar as alteraes dentro de um bloco
synchronized
Se os dados forem constitudos de apenas uma
varivel atmica, declarar a varivel como volatile*
* Garantido apenas para JVM 5.x em diante

Processos pseudo-atmicos
A linguagem garante que ler ou escrever em uma
varivel de tipo primitivo(exceto long ou double) um
processo atmico.
Portanto, o valor retornado ao ler uma varivel o valor exato
que foi gravado por alguma thread, mesmo que outras threads
modifiquem a varivel ao mesmo tempo sem sincronizao.

Cuidado com a iluso de atomicidade


private static int nextSerialNumber = 0;
public static int generateSerialNumber() {
return nextSerialNumber++;
}

O mtodo acima no confivel sem sincronizao


Por que? Como consertar?
5

Solues
/* Solucao 1: synchronized */
private static int nextSerialNumber = 0;
public static synchronized int generateSerialNumber() {
return nextSerialNumber++;
}
/* Solucao 2: objetos atmicos */
import java.util.concurrent.atomic.AtomicInteger;
private static AtomicInteger nextSerialNumber =
new AtomicInteger(0);
public static int generateSerialNumber() {
return nextSerialNumber.getAndIncrement();
}
/* Solucao 3: concurrent locks */
import java.util.concurrent.lock.*;
private static int nextSerialNumber = 0;
private Lock lock = new ReentrantLock();
public static int generateSerialNumber() {
lock.lock();
try { return nextSerialNumber++; }
finally { lock.unlock();}
}

Falha de comunicao
Esse problema demonstrado no padro comum usado para
interromper um thread, usando uma varivel booleana
Como a gravao e leitura atomica, pode-se cair na tentao de
dispensar a sincronizao
public class StoppableThread extends Thread {
private boolean stopRequested = false;
public void run() {
boolean done = false;
while (!stopRequested && !done) {
// ... do it
}
}
public void requestStop() {
stopRequested = true;
}
}

Por no haver sincronizao, no h garantia de quando o thread


que se quer parar ver a mudana que foi feita pelo outro thread!
Esse problema poder no acontecer em sistemas monoprocessados
7

Threads vs. objetos


main()
t1 = new ...
Stoppable
Thread
stoptrue
t1.start()
stop!

t1.run()

this
stop = true;

while( stop )

Solues
Uma soluo simplesmente sincronizar todos os
acessos ao campo usado na comunicao
A sincronizao neste caso est sendo usada apenas para seus
efeitos de comunicao (e no para excuso mtua)
public class StoppableThread extends Thread {
private boolean stopRequested = false;
public void run() {
boolean done = false;
while (!stopRequested() && !done) {
// ... do it
}
}
public synchronized void requestStop() {
stopRequested = true;
}
private synchronized boolean stopRequested() {
return stopRequested;
}
}

Solues (2)
Uma outra soluo declarar a varivel como volatile
O modificador volatile equivale a uma aquisio e liberao de
trava e tem a finalidade de resolver o problema da comunicao
entre threads
public class StoppableThread extends Thread {
private volatile boolean stopRequested = false;
public void run() {
boolean done = false;
while (!stopRequested && !done) {
// ... do it
}
}
public void requestStop() {
stopRequested = true;
}
Soluo recomendada
}

Java 5.0 em diante!

10

13. No use o multithreaded


Singleton anti-pattern* (JMM FAQ, EJ 48)
Esse famoso padro um truque para suportar
inicializao lazy evitando o overhead da sincronizao
Parece uma soluo inteligente (evita sincronizao no acesso)
Mas no funciona! Inicializao de resource (null) e
instanciamento podem ser reordenados no cache
class SomeClass {
private static Resource resource = null;
public static Resource getResource() {
if (resource == null) {
synchronized (Resource.class) {
if (resource == null)
resource = new Resource();
}
}
return resource;
}
}

* Tambm conhecido
como o double-check
idiom
11

O double-check idiom no funciona


Se um thread l a referncia sem sincronizao e depois
chama um mtodo no objeto referenciado, o mtodo
poder observar o objeto em um estado parcialmente
inicializado (e falhar)
Pode no parecer bvio o vazamento
O objeto construdo completamente antes da referncia ser
publicada em um campo onde poder ser lido por outros threads
Porm, sem sincronizao, a leitura da referncia de objeto
publicada no garante que o thread que l ver seus valores
mais recentes

O problema seria resolvido se


A varivel compartilhada fosse um inteiro, um byte ou outro
valor primitivo atmico
A varivel fosse volatile, garantindo a correta ordenao entre a
inicializao e construo do objeto, mas a performance seria
pior que antes (volatile funciona como um bloco synchronized)
12

Alternativas
No usar lazy instantiation
Melhor alternativa (deixar otimizaes para depois)
private static final Resource resource = new Resource();
public static Resource getResource() {
return resource;
}

Instanciamento lazy corretamente sincronizado


H custo de sincronizao em cada chamada
private static Resource resource = null;
public static synchronized Resource getResource() {
if (resource == null)
resource = new Resource();
return resource;
}
Esta tcnica explora a

Initialize-on-demand holder class idiom

garantia de que uma


classe no
private static class ResourceHolder {
inicializada antes que
static final Resource resource = new Resource();
seja usada.
}
public static Resource getResource() {
return ResourceHolder.resource;
}

13

3. Diferencie a sincronizao em
mtodos estticos e de instncia
(PJ)

Quando synchronized chamado em um


mtodo de instncia, o bloco ao qual se aplica a
trava restrito ao objeto
Outros objetos da mesma classe podem ser
livremente usados

Quando synchronized chamado em um


mtodo de classe (esttico) todo o estado da
classe travado
Impede que qualquer outro thread tenha acesso a
dados da classe inteira
14

4. Encapsule seus dados


No adianta usar synchronized se seus dados
estiverem incorretamente encapsulados
Declare o estado crtico de um objeto com modificador
private
No deixe referncias para dados de instncia vazarem
pelo construtor (no atribua this a variveis estticas
durante a construo do objeto)
No chame mtodos que possam ser sobrepostos
dentro de construtores (construa objetos corretamente!)

Exemplo de quebra de encapsulamento na


inicializao de instncias (a seguir)
15

Inicializao de instncias
O que acontece quando um objeto criado
usando new NomeDaClasse() ?
1. Inicializao default de atributos (0, null, false)
2. Chamada recursiva ao construtor da superclasse
(subindo at Object)
2.1 Inicializao default dos atributos de dados da superclasse
(recursivo, subindo a hierarquia)
2.2 Inicializao explicita dos atributos de dados
2.3 Execuo do contedo do construtor (a partir de Object,
descendo a hierarquia)

3. Inicializao explcita dos atributos de dados


4. Execuo do contedo do construtor
16

Exemplo (1)
class Bateria {
public Bateria() {
System.out.println("Bateria()");
}
}
class Tela {
public Tela() {
System.out.println("Tela()");
}
}
class Teclado {
public Teclado() {
System.out.println("Teclado()");
}
}

Mquina
ligar()

Computador
Teclado
Tela

Notebook
Bateria
codigo: 12345
ligar()

17

Exemplo (2)
class Maquina {
public Maquina() {
System.out.println("Maquina()");
this.ligar();
}
public void ligar() {
System.out.println("Maquina.ligar()");
}
}
class Computador extends Maquina {
public Tela tela = new Tela();
public Teclado teclado = new Teclado();
public Computador() {
System.out.println("Computador()");
}
}

Mquina
ligar()

Computador
Teclado
Tela

Notebook
Bateria
codigo: 12345
ligar()

18

Exemplo (3)
class Notebook extends Computador {
int codigo = 12345;
public Bateria bateria = new Bateria();
public Notebook() {
System.out.print("Notebook(); " +
"codigo = "+codigo);
}
public void ligar() {
System.out.println("Notebook.ligar();" +
" codigo = "+ codigo);
}
}
public class Run {
public static void main (String[] args) {
new Notebook();
}
}

Mquina
ligar()

Computador
Teclado
Tela

Notebook
Bateria
codigo: 12345
ligar()

19

new Notebook()
Maquina()

Isto foi executado, e...


1. Construtor de Maquina chamado
2. Mtodo ligar() de Notebook
(e no de Maquina) chamado!

Notebook.ligar(); codigo = 0
Tela()
Teclado()
Computador()
Bateria()
Notebook();

4. Variveis tela e teclado,


membros de Computador
so inicializadas

3. PROBLEMA!!!!!
Varivel codigo, de Notebook
ainda no foi inicializada
quando ligar() foi chamado!

5. Construtor de Computador chamado


6. Varivel bateria, membro
de Notebook inicializada
7. Construtor de Notebook
Varivel codigo
codigo = 12345 chamado.
finalmente inicializada
20

N1. new Notebook() chamado


N2. varivel cdigo iniciaizada: 0
N3. varivel bateria iniciaizada: null
N4. super() chamado (Computador)
C1. varivel teclado iniciaizada: null
C2. varivel tela iniclaizada: null
C3. super() chamado (Maquina)
M2. super() chamado (Object)
M2. Corpo de Maquina() executado:
println() e this.ligar()
C4: Construtor de Teclado chamado
Tk1: super() chamado (Object)
C5. referncia teclado inicializada
C6: Construtor de Tela chamado
Te1: super() chamado (Object)

Detalhes
O1. Campos inicializados
O2. Corpo de Object() executado

Efeito de
new Notebook()
Mquina
ligar()

Computador
teclad
o
tela

C7: referncia tela inicializada


C8: Corpo de Computador()
executado: println()
N5. Construtor de Bateria chamado
B1: super() chamado (Object)
N6: varivel cdigo inicializada: 12345
N7: referncia bateria inicializada
N8. Corpo de Notebook() executado: println()

Object

Notebook
bateria
codigo:
int
ligar()

Teclado

Tela

Bateria

21

N1. new Notebook() chamado


N2. varivel cdigo iniciaizada: 0
N3. varivel bateria iniciaizada: null
N4. super() chamado (Computador)
C1. varivel teclado iniciaizada: null
C2. varivel tela iniclaizada: null
C3. super() chamado (Maquina)
M2. super() chamado (Object)
M2. Corpo de Maquina() executado:
println() e this.ligar()
C4: Construtor de Teclado chamado
Tk1: super() chamado (Object)
C5. referncia teclado inicializada
C6: Construtor de Tela chamado
Te1: super() chamado (Object)
C7: referncia tela inicializada
C8: Corpo de Computador()
executado: println()

Quebra de
encapsulamento!
Mtodo ligar() chamado no
construtor de Maquina, mas ...
... a verso usada a
implementao em Notebook, que
imprime o valor de cdigo (e no a
verso de Maquina como aparenta)
Como cdigo ainda no foi
inicializado, valor impresso 0!

N5. Construtor de Bateria chamado


B1: super() chamado (Object)
N6: varivel cdigo inicializada: 12345
N7: referncia bateria inicializada
N8. Corpo de Notebook() executado: println()

22
Preste ateno nos pontos crticos!

Como evitar o problema?


Evite chamar mtodos locais dentro de construtores
Construtor (qualquer um da hierarquia) sempre usa verso
sobreposta do mtodo

Resultados inesperados se algum estender a sua


classe com uma nova implementao do mtodo que
Dependa de variveis da classe estendida
Chame mtodos em objetos que ainda sero criados (provoca
NullPointerException)
Dependa de outros mtodos sobrepostos
Deixe vazar variveis para campos estticos (em aplicaes
concorrentes, os valores so no determinsticos)

Use apenas mtodos finais em construtores


Mtodos declarados com modificador final no podem ser
sobrepostos em subclasses
23

5. Utilize sempre que possvel


(EJ 13)
objetos imutveis
So thread safe: no requerem sincronizao
No podem ser corrompidos por mltiplos threads concorrentes
Podem ser compartilhados livremente
Seu estado interno pode ser compartilhado

So simples
Um objeto imutvel pode estar em exatamente um estado: o estado
no qual foi criado
So ideais para servir de blocos de montagem para objetos
maiores, como elementos de conjuntos, chaves de mapas

Desvantagem: cada valor distinto requer um objeto prprio


Valores iguais so reutilizados (pools, caches)
Construir novos objetos requer criar vrios objetos durante o
processo para depois descart-los (ex: concatenao de String).
Soluo: prover uma classe companheira mutvel (ex: StringBuffer)
24

Recomendaes de design
Classes devem ser imutveis a menos que haja
uma boa razo para que no sejam
Pequenos objetos devem sempre ser imutveis
Considere fazer objetos maiores imutveis tambm
Fornea uma classe companheira mutvel somente
depois de confirmar que necessria para alcanar
performance satisfatria

Se uma classe no puder ser imutvel, voc


ainda deve limitar sua mutabilidade o quanto for
possvel
Construtores devem criar objetos completamente
inicializados e no passar instncias parcialmente
construdas a outros mtodos
25

Aplicaes
Pool de objetos
imutveis
compartilhados

26

if(flyweightPool.containsKey(id)) {
return (Flyweight)flyweightMap.get(id);
} else {
Flyweight fly = new FlyweightConcreto( genKey() );
flyweightPool.put(fly.getKey(), fly);
return fly;
}

FlyweightFactory
flyweightPool
getFlyweight(id)

Flyweight
GoF Design Pattern

interface

Flyweight
operacao(estadoMutavel)

FlyweightConcreto

FlyweightConcretoNaoCompartilhado

estadoImutavel

estadoMutavel

operacao(estadoMutavel)

operacao(estadoMutavel)

Cliente
27

Como garantir objetos imutveis


Ponto de partida: objetos puros
Mtodos puros no tm efeitos colaterais
Construtores puros s tem efeitos colaterais em estados recmalocados
Classes puras tm apenas mtodos puros, construtores puros e
subclasses puras

No garante imutabilidade!
public class Integer {
public int i;
public Integer(int j) {
i = j;
}
public int getValue() {
return i;
}
}

puro! No tem efeitos colaterais.


Mas no imutvel!

28

Como garantir objetos imutveis


Acesso direto ao estado do
public class Integer {
objeto foi fechado!
private int i;
public Integer(int j) {
i = j;
}
public int getValue() {
return i;
}
}

Classe aparentemente bem encapsulada, mas


ainda no imutvel!
Por que?

29

Final necessrio!
Processo de criao de um objeto
Objeto instanciado; atributos so inicializados a valores default (ex: 0)
Objetos e construtores das superclasses so inicializados
recursivamente
Atributos so inicializados a valores explcitos
Corpo do construtor executado (possvel nova atribuio)

Atributos assumem at 3 valores diferentes durante criao do objeto


Se i no for final, uma chamada new Integer(5) pode fazer com que o
valor de i aparea como 0 para alguns threads e 5 para outros
public class Integer {
private final int i;
public Integer(int j) {
i = j;
}
public int getValue() {
return i;
}
}

Funciona no Java 5.0 devido ao


novo modelo de memria (JMM)

30

No deixe vazar referncias na


construo do objeto!
Vazamento: this
Construtor deixou vazar um acesso
referncia do prprio objeto
public class Integer {
private final int i;
public static Integer ultimo;
public Integer(int j) {
i = j;
ultimo = this;
Threads que lem esta
}
referncia no tm garantia
public int getValue() {
de ver um valor fixo
return i;
}
}

31

Tipos de imutabilidade
Imutabilidade rasa
Imutabilidade profunda
Imutabilidade baseada em estado
Estado do objeto inclui apenas seus atributos
imutveis (referncias para objetos externas
no so parte de seu estado)

32

Imutabilidade rasa
Uma classe pura tem imutabilidade rasa se
1. Todos os campos de dados de instncia so finais, e
2. Construtores no deixam vazar a referncia this
public class BankTransfer {
private final char[] src, dest;
private final Integer amount;
...
Objetos no so
char[] getDest() {
imutveis, apenas as
return dest;
referncias!
}
Integer getAmount() {
return amount;
}
}
33

Imutabilidade profunda
Uma classe pura profundamente imutvel se
1. tiver imutabilidade rasa, e
2. todos os campos de dados que forem referncias
i. possuem tipos imutveis, ou
ii. no podem ser atribudas a outras referncias
public class BankTransfer {
private final BankAccount src, dest;
private final Integer amount;
...
Requer que destino e
BankAccount getDest() {
fonte da transferncia
return dest;
sejam imutveis!
}
BankAccount getSrc() {
Imutabilidade profunda
return amount;
excessiva neste caso
}
}
34

Imutabilidade baseada em estado

Soluo: meio-termo
Uma classe pura tem imutabilidade baseada
em estado se
1. todos os campos de instncia so finais, e
2. construtores no deixam vazar a referncia this, e
3. todos os atributos de instncia que tm referncias
i. possuem tipos imutveis, ou
ii. no podem ser atribudas a outras referncias (finais), ou
iii. so excludas do estado do objeto

35

6. Evite sincronizao excessiva


(EJ 49)
Sincronizao excessiva pode causar reduo de performance,
deadlock ou at mesmo comportamento no determinstico
O principal risco o deadlock. Para evit-lo
Nunca ceda controle ao cliente dentro de um mtodo ou bloco
sincronizado (no chame mtodos public ou protected que foram
criados para serem sobrepostos chame apenas mtodos final e
private)
Chame mtodos externos fora da regio sincronizada (open call). Alm
de prevenir deadlocks, open calls permitem aumento da concorrncia

Outro risco ocorre com o uso de travas recursivas


Mtodo externo, que retm a trava, chamado quando as invariantes
protegidas pela regio sincronizada esto temporariamente invlidas
Risco ocorre quando mtodo entra novamente na classe onde ele
retm a trava

Regra geral
Faa apenas o necessrio dentro de regies sincronizadas.
Obtenha a trava, analise os dados, altere o que precisar, libere a trava
36

public abstract class WorkQueue {


public final List queue = new LinkedList();
private boolean stopped = false;
protected WorkQueue() { new WorkerThread().start(); }
public final void enqueue(Object workItem) {
synchronized(queue) {
queue.add(workItem);
queue.notify();
}
}
public final void stop() {
synchronized(queue) {
stopped = true;
queue.notify();
}
}
protected abstract void processItem(Object workItem)
throws InterruptedException;
// Broken!
private class WorkerThread extends Thread {
public void run() {
while(true) {
synchronized(queue) {
try {
while(queue.isEmpty() && !stopped) queue.wait();
} catch (InterruptedException e) {
return;
}
if (stopped) return;
Object workItem = queue.remove(0);
try {
processItem(workItem); // lock held
} catch (InterruptedException e) {
return;
}}}}}}

Anti-pattern:
potencial de
deadlock

37

Deadlock Queue
Para usar a classe preciso implementar o mtodo
processItem
class DisplayQueue extends WorkQueue {
protected void processItem(Object
workItem)
throws InterruptedException {
System.out.println(workItem);
Thread.sleep(1000);
}}

Esta implementao causa deadlock


class DeadlockQueue extends WorkQueue {
protected void processItem(Object workItem)
throws InterruptedException {
Thread child = new Thread() {
public void run() { enqueue(workItem); }
};
child.start();
child.join(); // deadlock!
}}

M
38

Soluo
private class WorkerThread extends Thread { ...
public void run() {
while(true) {
synchronized(queue) {
try {
while(queue.isEmpty() && !stopped)
queue.wait();
} catch (InterruptedException e) {
return;
}
if (stopped) return;
Object workItem = queue.remove(0);
}
try {
processItem(workItem); // no lock held
} catch (InterruptedException e) {
return;
}}}}

Regra geral: para evitar corrupo de dados e deadlock, nunca


chame um mtodo externo de uma regio sincronizada
39

Thread-safe ou thread compatible?


1) Se voc est escrevendo uma classe que ser usada em
circunstncias que requerem sincronizao e tambm em
circunstncias onde ela no requerida
Oferea verses thread-safe (com sincronizao interna) e threadcompatible (no sincronizadas)
Uma forma de fazer isto atravs de uma classe Wrapper
(encapsuladora)

2) Para classes que no sero estendidas ou reimplementadas


Oferea uma classe no sincronizada e uma subclasse contendo
apenas mtodos sincronizados que chamam suas implementaes na
superclasse

3) Se uma classe ou mtodo esttico depende de um campo


esttico mutvel
Ele deve ser sincronizado internamente, mesmo que seja tipicamente
usado por um nico thread
40

Use a nova API


Considere a utilizao das implementaes da interface
Lock (pacote de utilitrios de concorrncia do Java 5.0)
O uso das APIs torna a ocorrncia de vrios dos problemas
descritos mais rara

A API oferece vrias implementaes eficientes de


travas; dentre elas
Implementaes de ReadWriteLock
ReentrantLock (efeito equivalente a synchronized)

Colees so implementadas em verses threadcompatible e thread-safe, por exemplo:


HashMap (thread-compatible)
Hashtable (thread-safe)
ConcurrentHashMap (thread-safe, da nova API)
41

7. Nunca chame wait() fora de


(EJ 50)
um loop
O mtodo Object.wait() usado para fazer a thread
esperar por uma determinada condio
Ele precisa ser chamado dentro de uma regio sincronizada que
trava o objeto em que chamado.
Tcnica padro para usar o mtodo wait:
synchronized (obj) {
while ( [condio no atendida] )
obj.wait();
// Realiza ao apropriada condio
}

Sempre chame use a tcnica padro para chamar wait()


Nunca chame-o fora de um loop
42

Viveza e segurana
Testar a condio antes de esperar e pular o
wait() se a condio foi atendida necessrio
para garantir a viveza (liveness) do thread.
Se a condio j foi atendida e um notify() j foi
chamado antes do thread comear a esperar, ele
pode nunca acordar do wait()

Testar a condio depois de esperar e esperar


de novo se a condio no foi atendida
necessrio para garantir a segurana
Se o thread continua com a ao quando a condio
no mais se aplica, pode destrur as invariantes
protegidas pela trava.
43

Acordando o thread fora de hora


H vrias razes que podem levar um thread a acordar
quando a condio do loop no for atendida
Outro thread pode ter obtido a trava e mudado o estado
protegido no intervalo em que um thread chamou notify() e o
thread que esperava acordou
Outro thread pode ter chamado notify() acidentalmente (ou
maliciosamente) quando a condio no fora atendida (qualquer
wait() contido em um bloco synchronized de um objeto
publicamente acessvel vulnervel a esse problema)
O thread notificador foi excessivamente generoso ao acordar
todos os threads com notifyAll()
O thread que espera poderia acordar na ausncia de uma
notificao (spurious wakeup)
44

notify vs. notifyAll

Use sempre o notifyAll()


Este o conselho mais conservador considerando que todas as chamadas ao
wait() esto dentro de loops,
Garante que as threads que precisam ser reativadas sempre sero reativadas.
Tambm sero reativadas outras threads, mas isso no influi no resultado do
programa, j que a condio ser testada antes de continuar qualquer execuo.

notifyAll() pode afetar a performance.


Em estruturas de dados onde algumas threads possuem um status especial e as
outras precisam esperar (nmero de threads saindo da espera quadrtico).

Para otimizar
Pode-se decidir usar o notify() ao invs do notifyAll() se todas as threads
esperam a mesma condio e apenas uma thread de cada vez pode se
beneficiar da condio tornar-se true.
Ainda assim pode ser recomendado utilizar o notifyAll(), pois protege o cdigo de
uma chamada indevida ao wait() por thread no relacionada

45

Use a nova API


D preferncia interface java.util.concurrent.locks.Lock
e Condition para implementar monitores
Condition.await() similar a wait()
Condition.signal()/signalAll() similar a notify()/notifyAll()

Veja exemplos na apresentao sobre utilitrios de


concorrncia

46

8. Nunca dependa do agendador


(EJ 51)
de threads

Depender do agendamento de threads criar aplicaes no portveis


Quando mltiplas threads so executveis, o agendador de threads
determina qual ir rodar e por quanto tempo.
A poltica usada varia muito de acordo com a implementao da JVM,
Para que o programa seja portvel, importante que ele no dependa do
agendador de threads para executar corretamente ou ter boa performance.

Para no depender demais de agendamento, uma aplicao multitarefa


deve ter o mnimo de threads executveis em qualquer momento.
A principal forma de atingir esse objetivo possuir threads que executam uma
quantidade de trabalho pequena e ento esperam por alguma condio (wait) ou
pela passagem de certo intervalo de tempo (sleep).

Evite usar yield()


Funciona, mas torna o programa no-portvel

Menos portvel ainda o ajuste de prioridade (setPriority())


Uma melhor abordagem reestruturar o programa para reduzir o nmero de
threads executveis ao mesmo tempo.

47

9. Documente a segurana em
situaes de concorrncia (EJ 52)
O comportamento de uma classe quando suas instncias e
mtodos estticos so expostos ao uso concorrente parte
importante do contrato que a classe estabelece com seus clientes
importante documentar esse comportamento
Caso no exista essa documentao, o usurio das suas classes tero
que tomar decises baseadas em suposies
Se as suposies estiverem erradas, erros srios podem resultar

No h como saber se um mtodo ou no synchronized olhandose apenas para a interface


Javadoc no indica o modificador synchronized
Blocos podem estar ocultos nos mtodos
synchronized apenas insuficiente para saber do nvel de segurana
que um thread suporta

Joshua Bloch (EJ) sugere cinco nveis de segurana a seguir


48

Nveis de segurana (1)


Immutable
Objetos so constantes e no podem ser mudados

Thread-safe
Objetos so mutveis mas podem ser usados com segurana em um
ambiente concorrente pois seus mtodos so sincronizados

Conditionally thread-safe
Objetos ou possuem mtodos que so thread-safe ou mtodos que so
chamados em seqncia com a trava mantida pelo cliente
preciso indicar quais sequencias de chamada requerem
sincronizao externa e quais travas precisam ser obtidas para impedir
acesso concorrente
importante tambm saber em qual objeto a trava obtida (pode ou
no ser o prprio objeto). Exemplo: private lock object idiom.

49

Nveis de segurana (2)


Thread-compatible
Instncias da classe no oferecem nenhuma sincronizao
Porm podem ser usadas com segurana em um ambiente de
concorrncia, se o cliente oferecer a sincronizao ao incluir
cada mtodo (ou seqncia de mtodos) em um bloco de trava
(synchronized ou similar)

Thread-hostile
Instncias da classe no devem ser usadas em um ambiente
concorrente mesmo que o cliente fornea sincronizao externa
Tipicamente, uma classe thread-hostile acessa dados estticos
ou o ambiente externo

50

10. Evite thread groups

M
(EJ 53)

Junto com threads, travas e monitores, uma abstrao bsica


oferecida pelo sistema de threads a de grupos de threads.
Servem para aplicar primitivos de Thread para vrias threads ao
mesmo tempo.
A maioria desses primitivos foi deprecada e os que restaram so muito
pouco usados

Grupos de threads no fornecem muita funcionalidade til.


Do ponto de vista de segurana ThreadGroup fraca
Vrios de seus mtodos no funcionam corretamente

ThreadGroup.uncaughtException()
o nico caso que justifica o uso de ThreadGroup
chamado automaticamente quando uma thread do grupo lana uma
exceo que no tratada.
O comportamento padro mostra a stack trace na sada padro de erro

Mas agora, Thread (Java 5.0) tem uncaughtException()


No h mais razo para usar ThreadGroup!

51

Use Executor
Executor (da nova API de concorrncia) oferece servios
de execuo de threads que possuem algumas das
caractersticas de Thread groups e mais vantagens
Pools de tamanho fixo que inicializam automaticamente vrios
threads
Pools com agendamento para inicializao de threads aps um
intervalo de tempo
Caches de tamanho varivel
Grupos de threads que precisam executar em seqncia (sem
concorrncia entre si, porm com possibilidade de concorrncia
com outros threads)
Suporte a objetos ativos que causam exceo e retornam valor
(Callable e Future)

52

11. Evite usar threads


(EJ 30)

Programao multithreaded mais difcil que programao


singlethreaded
Se houver uma classe de biblioteca que evitar que voc faa
programao de baixo nvel, use-a!

No usar threads do ponto de vista do programador - significa no


trabalhar diretamente com estruturas como synchronized, locks,
wait, notify, etc.
Threads so usados em todo lugar
No usar threads do ponto de vista da aplicao - implica em lidar
com eles (evitando a sincronizao)

Assim, no usar threads == usar APIs e frameworks que tornem


multithreading transparente
Aplicaes mais simples, mais fcil de usar e estender
Menos vulnervel a bugs, mais fcil de testar
Isso existe?
53

Fontes de referncia
[JLS] James Gosling, Bill Joy, Guy Steele, Java Language
Specification second edition, Addison Wesley, 2001
[SDK] Documentao do J2SDK 5.0
[EJ] Joshua Bloch, Effective Java Programming Guide, AddisonWesley, 2001
Vrios padres foram extrados deste livro; veja o item correspondente
em cada slide

[J133] Jeremy Manson and Brian Goetz, JSR 133 (Java Memory
Model) FAQ, Feb 2004
JSR 133 em forma de perguntas e respostas, com exemplos mais
compreensveis que na especificao.
www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html

[LEA2] Doug Lea, Synchronization and the Java Memory Model, 1999.
Um artigo compreensvel sobre o JMM (antigo, mas didtico)

[LEA1] Doug Lea, Concurrent Programming in Java (1st. ed), 1996


[BJ] Bruce Tate, Bitter Java, Manning, 2002
Vrios anti-patterns com refactorings que os consertam, destacando
situaes onde as regras podem (ou devem) ser quebradas
54

Threads em Java
Padres, anti-padres
e boas prticas

Criado em 31 de julho de 2005


55