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
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
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
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
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
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
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
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
e y
gravao
leitura
j = y;
}
void m2() {
y = 1;
gravao
leitura
i = x;
}
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
t2.start()
t2
t1
x = 1
y = 1
j = y
i = x
t1
j = y
t2.start()
t2
reordenando
y = 1
2
x = 1
i=0
j=0
i = x
24
25
Parte II
Como escrever
programas corretamente
sincronizados
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
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.
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
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
34
Falha de comunicao
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
}
&
38
Garantia de ordenao
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
&
&
&
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)
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()
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
Teclado
teclado
tela
Tela
Object
Notebook
bateria
codigo:
int
ligar()
Bateria
48
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!
49
Preste ateno nos pontos crticos!
Objetos imutveis
51
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
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
54