Anda di halaman 1dari 54

Tpicos

selecionados
de
programao
em

Java

Threads e o Modelo de
Consistncia de Memria
da Plataforma Java
Helder da Rocha

Agosto 2005

Objetivos
Apresentar os conceitos fundamentais do
Modelo de Consistncia de Memria da
plataforma Java (JMM Java Memory
Model)
Discutir estratgias para construir
programas corretamente sincronizados
usando a nova especificao da JMM

Parte I

O que o
Java Memory Model

O que um Modelo de
Consistncia de Memria?
Modelo que define os valores que podem ser retornados
na leitura de uma varivel compartilhada
necessrio para permitir otimizaes do compilador e
do processador
Otimizaes no afetam o modelo de memria de
sistemas uniprocessados
Em sistemas seqenciais, uma varivel sempre possui o valor
da ltima gravao

Mas os mesmos compiladores e processadores so


usados em sistemas paralelos
Em sistemas paralelos (multiprocessados, com buffers de
gravao paralelos, etc.) o valor de uma varivel pode ser
indeterminado devido a reordenaes e otimizaes agressivas
4

Modelos de Consistncia
de Memria
Consistncia Sequencial (CS)
Modelo mais restritivo: no permite reordenamento de
operaes de memria dentro de um thread
Equivalente a um sistema seqencial

Outros modelos relaxam a CS para melhorar a


performance
TSO (Total Store Order): permite que gravaes sejam feitas em
paralelo com leituras
PSO (Partial Store Order): permite que gravaes sejam feitas
em paralelo com outras gravaes
WO (Weak Ordering): permite leituras no bloqueadas
RC (Release Consistency): no garante a ordem entre
operaes de travamento e operaes de dados que a
antecedem (e vice-versa)
5

Modelos de Memria para


Linguagens
Modelos de memria para processadores e para
linguagens devem ser diferentes
Garantias de alto-nvel
Garantia de type-safety
Garantia de atomicidade de operaes em estruturas
invisveis aos programadores (ex: operaes em
ponteiros em Java)
Garantia de semntica especial (ex: volatile, final)
sem que seja necessrio usar recursos de baixo nvel
para garanti-las (memory barrrier)
Garantia de compatibilidade com transformaes
realizadas por compiladores
6

Modelo de Memria em
Linguagens
Descrevem relacionamento entre variveis de um
programa e a memria de um computador
As variveis so campos estticos, campos de instncia e
elementos de vetores
As operaes de memria so essencialmente a recuperao e
gravao de dados

O modelo de memria para Java deve garantir que


Seja impossvel um thread ver uma varivel antes que ela tenha
sido inicializada a seu valor default (0, null)
O fato que a coleta de lixo pode realocar uma varivel para uma
nova localidade imaterial e invisvel ao modelo de memria

Por que Java tem um Modelo de


Consistncia de Memria?
A linguagem Java suporta multithreading em memria
compartilhada
Threads podem executar em sistemas uniprocessados ou em
multiprocessados

Mas sistemas multiprocessados possuem diferentes


modelos de memria
Para garantir consistncia seqencial em todos eles seria
necessrio desabilitar certas otimizaes explicitamente
Isto no responsabilidade do programador Java
Contrato Write Once, Run Anywhere
Java no tem instrues MemBar (Memory Barrier)

Modelo de Memria do Java (JMM) garante uma


interface consistente, independente de plataforma, para
o programador de aplicaes
8

O que o Java Memory Model?


uma especificao
Parte do captulo 17 da Java Language Specification e captulo 8
da Java Virtual Machine Specification
Foi completamente reescrito para a verso 5.0! (JSR-133)

Define o conjunto de todos os comportamentos vlidos


que uma aplicao Java pode apresentar em qualquer
plataforma
No SC: Comportamentos vlidos podem produzir resultados
supreendentes (que violam a SC) parecido com RC!
Comportamentos vlidos podem ou no ser resultados de
programas corretos: a JMM define a semntica para programas
corretos (sem data-races) e incorretos
Permite otimizaes agressivas por parte dos multiprocessadores
e compiladores e previsibilidade por parte dos programadores
9

Por que a JMM foi reescrita?


Porque era excessivamente forte
Impedia otimizaes comuns nos processadores e compiladores
atuais
Era confusa e difcil de entender, e propunha controles muito
difceis de implementar (caros) o que levou a implementaes
incorretas

Porque era excessivamente fraca


No tinha uma semntica clara sobre programas incorretamente
sincronizados (no usar synchronized poderia produzir
resultados imprevisveis e incompatveis)
volatile no funcionava (no era clara a especificao e levou a
implementaes diferentes)
final no funcionava (poderia exibir valores diferentes ao longo
do programa conseqncia: String s no Java 5.0 imutvel)
10

Requerimentos do JMM
Definio de programas corretamente sincronizados
Garantir consistncia sequencial para programas corretamente
sincronizados (livres de data-races)
Programadores s precisam se preocupar que eventuais
otimizaes do sistema tero impacto no seu cdigo se esse
cdigo tiver data-races

Garantias para programas incorretos


O foco da reviso do modelo de memria no Java 5.0
Determina como cdigo deve se comportar quando no estiver
corretamente escrito sem limitar excessivamente as otimizaes
realizadas pelos compiladores e hardware existentes
Busca garantir que um valor inesperado no surja do nada (a
garantia baseada em implementaes atuais)
11

Comportamento de programas
corretamente sincronizados
Dentro de um mtodo ou bloco sincronizado, leitura de
memria compartilhada deve ler o valor da memria
principal
Antes que um mtodo ou bloco sincronizado termine, variveis
gravadas no seu interior devem ser escritas de volta na memria
principal

O sistema tem toda a liberdade para otimizar e reordenar


o cdigo de cada thread, desde que mantenha semntica
equivalente ao funcionamento sequencial
Em programas sequenciais, o programador no ser capaz de
detectar otimizaes e reordenaes
Em sistemas concorrentes, elas iro se manifestar, a no ser
que o programa esteja corretamente sincronizado
12

JMM: abstrao fundamental


Cada thread age como uma CPU virtual, que tem
acesso a um cache de memria local, e memria
principal que compartilhada entre todos os threads
Em multiprocessadores, as CPUs virtuais podem ser mapeadas
pela JVM a CPUs reais

O cache local usado para guardar cpias dos dados


que residem na memria principal compartilhada
A JMM uma abstrao de dados guardados em
registradores ou caches locais de um sistema
multiprocessado
O sistema real pode ser implementado de outra forma mas deve
comportar-se como a abstrao
13

Modelo de Memria do Java


Thread 1

Thread 2

Memria compartilhada

CPU
Cache local

CPU

monitorexit

Cpia
local

Cache local

monitorenter

Estado
do objeto
Cpia
local
Fonte da imagem: [8] e [9] (Doug Lea) 14

Garantias da JMM: trs aspectos


essenciais da sincronizao
Atomicidade
Travar objeto para obter excluso mtua durante
operaes crticas

Visibilidade
Garantir que mudanas a campos de objetos feitos
em um thread sejam vistos em outros threads

Ordenao
Garantir que no haver surpresa devido ordem em
que as instrues so executadas

15

JSR-133: Java Memory Model

Semntica informal: sincronizao


1. Todos os objetos Java agem como monitores que
suportam travas reentrantes
As nicas operaes que podem ser realizadas no monitor so
aes de travamento e destravamento
Uma ao de travamento bloqueia outros threads at que
consigam obter a liberao

2. Atomicidade: As aes em monitores e campos


volteis so executados de maneira seqencialmente
consistente
Existe uma nica, total e global ordem de execuo sobre essas
aes que consistente com a ordem em que as aes ocorrem
em seus threads originais
Aes sobre campos volteis so sempre imediatamente
visveis em outros threads, e no precisam ser guardados por
sincronizao
16

JSR-133: Java Memory Model

Semntica informal: sincronizao


3. Visibilidade: Se dois threads acessarem uma varivel, e
um desses acessos for uma gravao, ento o programa
deve ser sincronizado para que o primeiro acesso seja
visvel ao segundo
Quando um thread t1 adquire uma trava para um monitor m que
era previamente mantido por outro thread t2, todas as aes
visveis a t2 no momento da liberao de m tornam-se visveis a t1

4. Ordenao: Se um thread t1 inicia um thread t2, aes


visveis a t1 no momento em que ele inicia t2 tornam-se
visveis a t2 antes de t2 iniciar
Se t1 espera t2 terminar atravs de uma operao join(), todos os
acessos visveis a t2 quando terminar sero visveis a t1 quando o
join() terminar
17

JSR-133: Java Memory Model

Semntica informal: volatile e final


5. Semntica de volatile: Quando um thread t1 l um
campo volatile v que foi previamente gravado por um
thread t2, todas as aes que eram visveis a t2 no
momento em que t2 gravou em v tornam-se visveis a t1
Existe uma ordenao entre blocos sincronizados e campos
volteis

6. Semntica de final: Campos finais no podem retornar


valores diferentes em nenhuma fase de sua construo
Quando um campo final for lido, o valor lido o valor atribudo
no construtor (nunca ser retornado o tipo default do campo)
preciso garantir que o construtor para um objeto foi concludo
antes que outro objeto possa carregar uma referncia para esse
objeto
18

Conseqncias da nova JMM


Volatile pode ser usada para garantir que uma varivel
seja vista entre threads
Mas o custo da comunicao equivalente a um travamento e
destravamento (bloco synchronized)

Final garantido desde que um objeto seja construdo


corretamente
No deve haver vazamentos dentro de construtores

Sem sincronizao, o modelo de memria permite que o


processador ou compilador reordene as instrues
agressivamente
O modelo resultante no tem consistncia seqencial mas
permite otimizaes agressivas por parte do compilador e
processador
19

Algoritmo de Dekker
Clssico algortmo de programao concorrente para
excluso mtua
Como o modelo de memria no garante CS, o algoritmo no
funciona em Java, a menos que haja sincronizao
x = y = 0;
void m1() {

gravao
leitura

x = 1;
if(y == 0) { ... seo crtica ... }
}

gravao

void m2() {

leitura

y = 1;
if (x == 0) {... seo crtica ...}
}
20

Por que no funciona? (1)


Considere que um thread t1 executa o mtodo m1() e
um thread t2 executa o mtodo m2() concorrentemente
Quais so os valores possveis para as leituras de x e
y, ou seja, quais os valores que i e j podem ter?
x = y = 0; // valores iniciais de x
void m1() {
x = 1;

e y

gravao
leitura

j = y;
}
void m2() {
y = 1;

gravao
leitura

i = x;
}

O cdigo foi reescrito para destacar as leituras e gravaes


21

Cenrios possveis em CS
Inicialmente
x=y=0
Todos os cenrios
consideram CS

t1
1: x = 1
2: j = y

W
R

x
1
y
1

W
R

t2

t1

3: y = 1

1: x = 1

4: i = x

j = 1, i = 1
Thread t1:
1: x = 1;
2: j = y;
Thread t2:
3: y = 1;
4: i = x;

Mas a JMM no
garante CS!
Que valores so
possveis sem CS?

t1
1: x = 1

2: j = y

x
1
y
0

t2
W
R

2: j = y
t

x
1
y
1

4: i = x

2: j = y

t2
3: y = 1

1: x = 1

4: i = x

2: j = y

j = 1, i = 1

x
0
y
1

3: y = 1
4: i = x

j = 1, i = 0
t2

t1

4: i = x

t2

t1

3: y = 1

j = 1, i = 1

1: x = 1

t1
W

t2

3: y = 1

j = 0, i = 1

1: x = 1

2: j = y

x
1
y
1

x
1
y
1

3: y = 1

4: i = x

j = 1, i = 1

22

Lgica estranha em Java


x = y = 0
t1.start()

t2.start()

t2

t1
x = 1

y = 1

j = y

i = x

Poderia o resultado ser: i = 0 e j = 0?


23

O JMM garante que poderia sim!


Viola a consistncia seqencial
Resultado i = 0 e j = 0 no seria possvel em sistemas CS

Mas a JMM no garante CS!


Compilador pode reordenar instrues independentes
Processador pode usar buffers de gravao para leitura
paralela se a leitura no depender da gravao
x = y = 0
t1.start()

t1
j = y

t2.start()

t2

reordenando

y = 1
2

x = 1

i=0
j=0

i = x
24

Como isto pode acontecer?


1. O compilador pode reordenar instrues ou manter os
valores nos registradores
Diversas tcnicas que visam melhorar eficincia de algoritmos
No afeta funcionamento sequencial

2. O processador pode reorden-los


3. Em sistemas multiprocessados, os valores no so
sincronizados na memria global

O JMM projetado para permitir otimizaes agressivas


Inclusive otimizaes que ainda no foram implementadas
timo para performance;
Ruim para raciocinar sobre cdigo no sincronizado

25

Para que preciso do JMM?


Entender os detalhes do Java Memory Model
necessrio se voc precisar
Construir um compilador, uma mquina virtual
Simular a mquina virtual
Escrever programas em Java que dispensam o uso de
synchronized (no recomendado)

Entender os princpios bsicos do Java Memory Model


importante para
Usar corretamente synchronized, final e volatile e escrever
aplicaes que funcionem em qualquer plataforma
O comportamento do Java Memory Model s complexo em
programas incorretamente sincronizados
26

Como usar corretamente o JMM?


Usar corretamente o JMM no difcil. preciso
Entender o significado da palavra synchronized
Lidar corretamente com dados que so mutveis e
compartilhados
Entender o processo de construo de objetos para
garantir a correta semntica de final e construir
objetos realmente imutveis
Entender o custo de performance das operaes de
sincronizao e volatile

Escrever cdigo multithreaded no simples


Use os utilitrios de concorrncia do Java 5.0 sempre
que possvel e evite a programao em baixo nvel
27

Parte II

Como escrever
programas corretamente
sincronizados

O que significa synchronized?


Synchronized no s uma trava
Para entender corretamente synchronized preciso
entender o modelo de memria
No modelo de memria do Java, cada CPU (real ou virtual) tem
uma cpia dos dados compartilhados em cache
No h garantia que a cpia local esteja em dia com os dados
compartilhados a no ser que acessos estejam em um bloco
synchronized, ou sejam via 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
29

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
Thread 1

Thread 2
Memria compartilhada

CPU
Cache

CPU
synchron

Cpia
local

ized {

Cache

(objeto)

Cpia
local

Fonte: [9]

30

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?
31

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();}
}

32

Dados mutveis e
compartilhados
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

33

Liberao e aquisio
Todos os acessos antes de uma liberao so
ordenadas e visveis a quaisquer novos acessos
aps uma aquisio correspondente
O thread v os efeitos de todos os outros acessos
sincronizados

O destravamento de um monitor/trava uma


liberao, que adquirida por qualquer trava
seguinte daquele monitor/trava

34

Falha de comunicao

Esse problema demonstrado no padro comum usado para


interromper um thread, usando uma varivel booleana
Como a gravao e leitura atmica, 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 nunca acontecer em monoprocessadores
35

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;
}
}

&

36

Campos volteis
Se um campo puder ser simultaneamente
acessado por mltiplos threads, e pelo menos
um desses acessos for uma operao de
gravao, faa o campo voltil
O que faz volatile?
Leituras e gravaes vo diretamente para a
memria: no cacheado nos registros
Longs e doubles volteis so atomicos (longs e
doubles normais no so: apenas tipos primitivos
menores o so)
Reordenamento de acessos volteis pelo compilador
limitado
37

Garantia de visibilidade
A soluo ideal 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!

38

Garantia de ordenao

Se um thread l data, h uma liberao/aquisio em ready que


garante visibilidade e ordenao
class Future {
private volatile boolean ready;
private Object data;
public Object get() {
if (!ready)
return null;
return data;
}
public synchronized void setOnce(Object o) {
if (ready) throw ;
data = o;
ready = true;
Gravao vai sempre
}
acontecer antes devido
}
ordenao!
39

Outras aes que causam


liberao/aquisio
Outras aes tambm formam pares
libera-adquire
Iniciar um thread uma liberao adquirida
pelo mtodo run() do thread
Finalizao de um thread uma liberao
adquirida por qualquer thread que junta-se
(joins) ao thread terminado

40

O famigerado multithreaded
(JMM FAQ, EJ 48)
Singleton anti-pattern
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
41

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 (somente Java 5.0)


H custo de sincronizao na varivel voltil
private volatile static Resource resource = null;
public static Resource getResource() { ( to lento quanto declarar o
if (resource == null)
mtodo getResource
resource = new Resource();
synchronized)
return resource;
}
Esta tcnica explora a
garantia de que uma
Initialize-on-demand holder class idiom
classe no
private static class ResourceHolder {
inicializada antes que
static final Resource resource = new Resource();
seja usada.
}
public static Resource getResource() {
return ResourceHolder.resource;
}

&

&

42

Inicializao de instncias
O que acontece quando um objeto criado
usando new Classe() ?
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
43

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

Mquina

class Tela {
public Tela() {
System.out.println("Tela()");
}
}

Computador

class Teclado {
public Teclado() {
System.out.println("Teclado()");
}
}

ligar()

Teclado
Tela

Notebook
Bateria
codigo: 12345
ligar()

44

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()

45

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()

46

new Notebook()

Isto foi executado, e...


1. Construtor de Maquina chamado

Maquina()

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
codigo = 12345 chamado. Varivel codigo
finalmente inicializada
47

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

B1: super() chamado (Object)


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

Teclado

teclado
tela
Tela

C7: referncia tela inicializada


C8: Corpo de Computador()
executado: println()
N5. Construtor de Bateria chamado

Object

Notebook
bateria
codigo:
int
ligar()

Bateria

48

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()

49
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 (comportamento
de final s garantido na concluso do Construtor!)

Use apenas mtodos finais em construtores


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

Objetos imutveis

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)

51

No deixe vazar referncias na


construo do objeto!
Vazamento: this
Construtor deixou vazar um acesso referncia do prprio
objeto: final falha!
JMM garante semntica de final para objetos construdos
corretamente (ou seja, sem deixar vazar referncias no
construtor)
public class Integer {
private final int i;
public static Integer ultimo;
public Integer(int j) {
i = j;
Threads que lem esta
ultimo = this;
referncia no tm garantia
}
de ver um valor fixo
public int getValue() {
return i;
Bug semelhante: criar novo
}
Thread dentro do construtor!
}

52

Concluses
O JMM foi reescrito para garantir uma semntica
simples e determinstica para o funcionamento de
programas (corretos e incorretos) aos programadores
Programas incorretamente sincronizados podem causar
resultados surpreendentes, porm vlidos pelo JMM

O JMM garante a semntica esperada de volatile, final


e synchronized para programas corretos
Construir programas corretos requer
Conhecimento do funcionamento de volatile e synchronized para
construir programas corretamente sincronizados
Construir objetos corretamente (garantindo encapsulamento
total do construtor durante a criao de objetos)
53

Fontes de referncia
[1] James Gosling, Bill Joy, Guy Steele, Java Language Specification third
edition, Addison Wesley, 2005 Captulo 17
[2] Jeremy Manson, William Pugh. JSR-133 The Java Memory Model and
Thread Specification.
[3] William Pugh. Fixing the Java Memory Model.
[4] Sarita Adve et al. Shared Memory Consistency Models: A Tutorial
[5] Documentao do J2SDK 5.0
[6] Joshua Bloch, Effective Java Programming Guide, Addison-Wesley, 2001
[7] Jeremy Manson and Brian Goetz, JSR 133 (Java Memory Model) FAQ, 2004
www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html

[8] Doug Lea, Synchronization and the Java Memory Model, 1999.
[9] Doug Lea, Concurrent Programming in Java (1st. ed), 1996

2005, Helder da Rocha


www.argonavis.com.br

54

Anda mungkin juga menyukai