Documente Academic
Documente Profesional
Documente Cultură
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)
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
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]
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.
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;
}
}
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
}
10
* Tambm conhecido
como o double-check
idiom
11
Alternativas
No usar lazy instantiation
Melhor alternativa (deixar otimizaes para depois)
private static final Resource resource = new Resource();
public static Resource getResource() {
return resource;
}
13
3. Diferencie a sincronizao em
mtodos estticos e de instncia
(PJ)
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)
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()
Notebook.ligar(); codigo = 0
Tela()
Teclado()
Computador()
Bateria()
Notebook();
3. PROBLEMA!!!!!
Varivel codigo, de Notebook
ainda no foi inicializada
quando ligar() foi chamado!
Detalhes
O1. Campos inicializados
O2. Corpo de Object() executado
Efeito de
new Notebook()
Mquina
ligar()
Computador
teclad
o
tela
Object
Notebook
bateria
codigo:
int
ligar()
Teclado
Tela
Bateria
21
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!
22
Preste ateno nos pontos crticos!
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
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
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
No garante imutabilidade!
public class Integer {
public int i;
public Integer(int j) {
i = j;
}
public int getValue() {
return i;
}
}
28
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)
30
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
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
Regra geral
Faa apenas o necessrio dentro de regies sincronizadas.
Obtenha a trava, analise os dados, altere o que precisar, libere a trava
36
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);
}}
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;
}}}}
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()
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
46
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
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
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
M
(EJ 53)
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
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
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)
Threads em Java
Padres, anti-padres
e boas prticas