Resenhas sobre Concorrência em Java na Prática

Propaganda
Resenhas sobre
Concorrência em Java na Prática
Eu tive a sorte, realmente, de ter trabalhado com um time fantástico no projeto e na implementação das
funcionalidades de concorrência acrescentadas à plataforma Java no Java 5.0 e no Java 6.0. Agora, este
mesmo time fornece a melhor explanação dessas novas funcionalidades e de concorrência em geral.
Concorrência não é mais um assunto apenas para usuários avançados. Todo desenvolvedor Java deveria
ler este livro.
Martin Buchholz
Czar da Concorrência em Java, Sun Microsystems
Nos últimos trinta anos a performance dos computadores tem sido determinada pela Lei de Moore;
de agora em diante será determinada pela Lei de Amdahl. Escrever um código que efetivamente
explore múltiplos processadores pode ser bastante desafiador. Concorrência em Java na Prática apresenta os
conceitos e técnicas necessárias para escrever programas em Java seguros e escaláveis para os sistemas
de hoje – e de amanhã.
Doron Raijwan,
Cientista pesquisador, Intel Corp.
Este é o livro que você precisa se está escrevendo – ou projetando, ou depurando, ou mantendo, ou
analisando – programas Java com múltiplos threads. Se você já precisou alguma vez sincronizar um
método e não estava certo do motivo, você deve a si mesmo e a seus usuários uma leitura minuciosa
deste livro.
Ted Neward
Autor de Effective Enterprise Java (Java Eficaz para Organizações)
Brian endereça as questões fundamentais e as complexidades da concorrência com uma clareza fora
do comum. Este livro é leitura obrigatória para qualquer um que use threads e se preocupe com
performance.
Kirk Pepperdine
CTO, JavaPerformanceTunnin.com
Este livro cobre um tópico muito profundo e delicado de um jeito muito claro e conciso, fazendo-se
o perfeito manual de referência da concorrência em Java. Cada pagina está repleta de problemas (e
soluções!) com os quais os programadores lidam diariamente. Explorar a concorrência de maneira eficaz
vem se tornando cada vez mais importante agora que a Lei de Moore vem produzindo mais núcleos,
porém não mais velozes, e este livro irá mostrar como fazer isso.
DR. Cliff Click
Engenheiro de Software Sênior, Azul Systems
Eu tenho um grande interesse em concorrência e devo, provavelmente, ter escrito mais threads de
execução infinita e cometido mais erros de sincronização do que a maioria dos programadores. O livro
de Brian é o mais legível no tópico de uso de threads e concorrência em Java e lida com este difícil tema
com uma maravilhosa abordagem de ‘mão na massa’. Este é um livro que eu recomendo aos meus
leitores na The Java Specialists’ Newsletter, porque é interessante, útil e relevante diante dos problemas
enfrentados pelos desenvolvedores Java de hoje.
Dr. Heinz Kabutz
The Java Specialists’ Newsletter (Lista dos Especialistas em Java)
Eu foquei minha carreira em simplificar problemas simples, mas este livro trabalha de forma ambiciosa
e eficaz para simplificar um assunto complexo e crítico: concorrência. Concorrência em Java na Prática é
revolucionário na sua abordagem, de estilo suave e fácil, e veio na hora certa – está destinado a ser um
livro muito importante.
Bruce Tate
Autor de Beyond Java (Além do Java)
Concorrência em Java na Prática é uma compilação de conhecimentos de valor inestimável em thread para
desenvolvedores Java. Achei a leitura deste livro intelectualmente excitante, em parte porque ele é uma
excelente introdução a API de concorrência em Java, mas principalmente porque ele captura de maneira
completa e acessível conhecimento especialista em threads, o que não se acha facilmente por aí.
Bill Venners
Autor de Inside the Java Virtual Machine (Por dentro da Máquina Virtual Java)
Java Concorrente na Prática
Brian Goetz
Tim Peierls
Joshua Bloch,
Joseph Bowbeer
David Holmes
e Doug Lea
Para Jéssica
Conteúdo
Listagem .......................................................................................................................................................xiii
Prefácio ........................................................................................................................................................xvii
1
Introdução
1
1.1 Uma (muito) breve história da concorrência ........................................................................ 1
1.2 Benefícios das threads ............................................................................................................... 2
1.3 Riscos de threads ....................................................................................................................... 4
1.4 Threads estão por toda parte .................................................................................................. 7
I
Fundamentos
11
2
Segurança em relação a threads
13
2.1 Oque é segurança em relação a threads? ............................................................................. 14
2.2 Atomicidade ............................................................................................................................. 16
2.3 Acesso exclusivo ...................................................................................................................... 19
2.4 Protegendo estado com travas .............................................................................................. 22
2.5 Ativaçaõ e performance ......................................................................................................... 24
3
Objetos compartilhados
27
3.1 Visibilidade ................................................................................................................................ 27
3.2 Publicação e vazamento .......................................................................................................... 32
3.3 Confinamento de threads ........................................................................................................ 34
3.4 Imutabilidade ............................................................................................................................ 37
3.5 Publicaçaõ segura ..................................................................................................................... 40
4
Objetos compostos
45
4.1 Projetando classes seguras em relação às threads ............................................................... 45
4.2 Confinamento de instância ..................................................................................................... 48
4.3 Delegando a segurança em relação às threads .................................................................... 51
4.4 Adicionando funcionalidades a classes existentes e seguras
em relação às threads .................................................................................................................... 57
4.5 Documentando políticas de sincronização ......................................................................... 60
X
5
Conteúdo
Ferramentas
63
5.1 Coleções sincronizadas .............................................................................................................63
5.2 Coleções concorrentes ............................................................................................................ 67
5.3 Filas com bloqueio e o padrão produtor-consumidor4 ...................................................... 69
5.4 Métodos que podem ser bloqueados ou interrompidos .................................................... 73
5.5 Sincronizadores ........................................................................................................................ 74
5.6 Construindo uma cache de resultados escalável e eficiênte ............................................... 80
II Fundamentos
87
6
89
Execução de tarefas
6.1 Executando tarefas em threads ............................................................................................. 89
6.2 A infra-estrutura Executor .................................................................................................... 92
6.3 Encontrando paralelismo explorável ................................................................................... 97
7
Cancelamento e desligamento
105
7.1 Cancelamento de tarefa ......................................................................................................... 105
7.2 Parando um serviço baseado em threads ........................................................................... 116
7.3 Lidando com o término anormal de threads .................................................................... 124
7.4 Desligando a JVM ................................................................................................................. 126
8
Utilizando reservatórios de threads
129
8.1 Acoplamentos implícitos entre tarefas e políticas de execução ............................................... 129
8.2 Dimensionando reservatórios de threads ........................................................................... 131
8.3 Configurando o ThreadPoolExecutor ................................................................................ 132
8.4 Estendendo um ThreadPoolExecutor ............................................................................... 138
8.5 Paralelizando algoritmos discurso ....................................................................................... 139
9
Aplicações GUI
147
9.1 Por que interfaces gráficas possuem uma única thread? .................................................. 147
9.2 Tarefas de GUI de curta duração ........................................................................................ 149
9.3 Tarefas de GUI de longa duração ....................................................................................... 151
9.4 Modelos de dados compartilhados .................................................................................... 154
9.5 Outras formas de subsistemas de thread única ................................................................. 157
Conteúdo
XI
III Ativação, performance e teste
159
10
161
Evitando Perigos de Ativação
10.1 Deadlock ............................................................................................................................... 161
10.2 Evitando e diagnosticando deadlock ................................................................................ 168
10.3 Outros perigos de ativação ................................................................................................. 170
11
Performance e Escabilidade
173
11.1 Pensando sobre performance ........................................................................................... 173
11.2 Lei de Amdahl .................................................................................................................... 17
11.3 Custos introduzidos por threads ...................................................................................... 179
11.4 Reduzindo a disputa por travas ....................................................................................... 182
11.5 Exemplo: Comparando a performance de Map ............................................................ 189
11.6 Reduzindo a sobrecarga da troca de contexto .............................................................. 190
12
Testando Programas Concorrentes
193
12.1 Testando corretude ............................................................................................................. 194
12.2 Testando performance ....................................................................................................... 203
12.3 Evitando armadilhas em testes de performance ............................................................ 207
12.4 Abordagens complementares de teste ............................................................................ 211
IV Fundamentos
215
13
217
Travas Explícitas
13.1 Lock e ReentrantLock ....................................................................................................... 217
13.2 Considerações de performance ........................................................................................ 220
13.3 Regularidade ................................................................................................................... 221
13.4 Escolhendo entre synchronized e ReentrantLock ........................................................ 223
13.5 Travas de leitura-escrita ...................................................................................................... 223
14
Construindo Sincronizadores Personalizados
227
14.1 Gerenciando a dependência de estado ........................................................................... 227
14.2 Usando filas condicionais .................................................................................................. 232
14.3 Objetos de condições explícitas ...................................................................................... 238
14.4 Anatomia de um sincronizador ........................................................................................ 240
14.5 AbstractQueuedSynchronizer .......................................................................................... 242
14.6 AQS em classes sincronizadoras de java.util.concurrent ...............................................244
XII
Conteúdo
15
Variáveis Atômicas e Sincronização sem Bloqueio
247
15.1 Desvantagens do travamento ........................................................................................... 247
15.2 Suporte do hardware para concorrência ......................................................................... 248
15.3 Classes de variáveis atômicas ............................................................................................ 251
15.4 Algoritmos sem bloqueio ................................................................................................. 255
16
O Modelo de Memória Java
261
16.1 O que é um modelo de memória e por que eu ia querer um? .. ..............................................261
16.2 Publicação ............................................................................................................................. 267
16.3 Segurança de inicialização ................................................................................................... 270
Listagens
1 Maneira ruim de ordenar uma lista. Não faça isso.
2 Maneira não tão boa de ordenar uma lista.
1.1 Gerador de seqüência sem thread-safe.
1.2 Gerador de seqüências thread-safe.
2.1 Uma servlet stateless.
2.2 Servlet que conta as solicitações sem a necessária sincronização. Não faça isso.
2.3 Condição de corrida em inicialização tardia. Não faça isso.
2.4 Servlet que contabiliza as solicitações usando o AtomicLong.
2.5 Servlet que tenta guardar em cache seu último resultado sem a adequada atomicidade. Não faça
isso.
2.6 Servlet que armazena o último resultado, mas com uma concorrência inaceitavelmente pobre. Não
faça isso.
2.7 Código que iria sofrer deadlock se as travas intrínsecas não fossem reentrantes.
2.8 Servlet que armazena a última solicitação e o último resultado.
3.1 Compartilhando variáveis sem sincronização. Não faça isso.
3.2. Um detentor de inteiro mutável sem thread-safe.
3.3. Um detentor de inteiro mutável com thread-safe.
3.4. Contando carneirinhos.
3.5. Publicando um objeto.
3.6. Permitindo que o estado mutável interno vaze. Não faça isso.
3.7. Permitindo implicitamente que a referência this vaze. Não faça isso.
3.8. Usar um método de fabricação para evitar que a referência this vaze durante a construção.
3.9. Confinamento a thread de variáveis locais primitivas e referências.
3.10. Usando ThreadLocal para garantir confinamento de threads.
3.11. Classe imutável construída a partir de objetos mutáveis.
3.12. Objeto imutável para guardar um número e seus fatores.
3.13. Guardando o último resultado usando uma referência volátil para um objeto imutável.
3.14. Publicando um objeto sem sincronização adequada. Não faça isso.
3.15. Classe com risco de falha se não for apropriadamente publicada.
4.1.Um simples contador thread-safe usando o padrão Java monitor.
4.2. Usando o confinamento para garantir segurança em relação a threads.
4.3. Protegendo o estado com uma trava privada.
4.4. Implementação do rastreador de veículos baseada em monitores.
4.5. Classe de ponto mutável, similar à java.awt.Point.
4.6. Classe Point imutável utilizada por DelegatingVehicleTracker.
4.7. Delegando a segurança em thread a ConcurrentHashMap.
4.8. Retornando uma cópia estática do conjunto de localizações em vez de um “vivo”.
4.9. Delegando a segurança em relação a threads a múltiplas variáveis de estado.
4.10. Classe de faixa de números que não protege suas invariantes o suficiente. Não faça isso.
4.11.Classe de pontos mutáveis thread-safe.
4.12. Rastreador de veículos que publica de modo seguro o seu estado interno.
4.13 Estendendo Vector para possui um método adiciona-se-não-existir.
4.14. Tentativa de implementar uma operação de adicione-se-não-existir sem segurança em relação a
threads. Não faça isso.
4.15. Implementando a adicione-se-não-existir com travamento do lado cliente.
4.16. Implementando um “adicione-se-não-existir” usando composição.
5.1. Ações compostas em um Vector que podem produzir resultados confusos.
5.2. Ações compostas em Vector utilizando travamento do lado cliente.
5.3. Iteração que pode lançar ArrayIndexOutOfBoundsException.
5.4. Iteração com travamento do lado cliente.
5.5. Iterando uma List com um Iterator.
5.6. Iteração oculta em uma concatenação de texto. Não faça isso.
5.7. Interface ConcurrentMap.
5.8. Tarefas de produtor e consumidor em uma aplicação de varredura de área de trabalho.
5.9. Iniciando a varredura da área de trabalho.
5.10. Restaurando o estado de interrupção de modo que a interrupção não seja ocultada.
5.11. Usando CountDownLatch para iniciar e parar threads em testes de contabilização de tempo.
5.12. Usando FutureTask para carregar antecipadamente os dados que serão requeridos mais tarde.
5.13. Coagindo um Throwable não checado a uma RuntimeException.
5.14. Usando Semaphore para limitar uma coleção.
5.15. Coordenando a computação em um autômato celular com CyclicBarrier.
5.16. Tentativa inicial de cache usando HashMap e sincronização.
5.17. Substituindo HashMap por ConcurrentHashMap.
5.18. Classe-nvoltório de memoizador usando FutureTask.
5.19. Implementação final de Memoizer.
5.20.Servlet de fatoração que armazena os resultados em cache usando Memoizer.
6.1. Servidor Web seqüencial.
6.2. Servidor web que inicia uma nova thread para cada solicitação.
6.3. Interface Executor.
6.4 Servidor Web usando um reservatório de threads.
6.5 Executor que inicia uma nova thread para cada tarefa.
6.6 Executor que executa tarefas sincronamente na thread chamadora.
6.7. Métodos de ciclo de vida em ExecutorService.
6.8. Servidor web com suporte a desligamento.
6.9. Classe ilustrando o comportamento confuso de Timer.
6.10 Renderizando elementos de página seqüencialmente.
6.11 Listagem 6.11 Interfaces Callable e Future.
6.12. Implementação padrão de um newTaskFor em ThreadPoolExecutor.
6.1.3. Esperando pela carga de imagens com Future.
6.14. Classe QueueingFuture usada por ExecutorCompletionService.
6.15 Usando CompletionService para renderizar elementos de página à medida que eles são
disponibilizados.
6.16. Obtendo um anúncio com um orçamento de tempo.
6.17. Solicitando ofertas de viagem sob um orçamento de tempo.
7.1. Usando um campo volatile para armazenar o estado de cancelamento
7.2. Gerando quantos números primos for possível em um segundo.
7.3. Cancelamento não confiável que deixa produtores parados em uma operação com bloqueio. Não
faça isso.
7.4. Métodos de interrupção em Thread.
7.5. Usando interrupções para cancelamento.
7.6. Propagando InterruptedException para chamadores.
7.7. Tarefa não passível de cancelamento que restaura a interrupção antes de finalizar.
7.8. Agendando uma interrupção em uma thread emprestada. Não faça isso.
7.9. Interrompendo uma tarefa em uma thread dedicada.
7.10. Cancelando uma tarefa usando Future.
7.11. Encapsulando cancelamento fora de padrão em uma Thread sobrescrevendo interrupt.
7.12. Encapsulando cancelamento fora de padrão em uma tarefa com newTaskFor.
7.13 Serviço de mensageria produtor-consumidor sem suporte a desligamento.
7.14. Modo não confiável de adicionar suporte ao desligamento de um serviço de mensageria.
7.15. Adicionando cancelamento confiável a LogWriter.
7.16. Serviço de mensageria que usa um ExecutorService.
7.17. Desligamento com presente envenenado.
7.18. Thread produtora para IndexingService.
7.19. Thread consumidora para IndexingService.
7.20. Usando um Executor privado, cujo tempo de vida é limitado pela chamada de um método.
7.21. ExecutorService que mantém o rastro de tarefas canceladas após o desligamento.
7.22. Usando TrackingExecutorService para salvar tarefas não finalizadas para execução futura.
7.23. Estrutura típica de thread trabalhadora de um reservatório de threads.
7.24. Interface UncaughtExceptionHandler.
7.25. UncaughtExceptionHandler que registra a exceção.
7.26. Registrando uma pendência de desligamento para parar o serviço de mensageria.
8.1. Tarefa que deadlock em um Executor de thread única. Não faça isso.
8.2. Construtor comum de ThreadPoolExecutor.
8.3. Criando um reservatório de threads de tamanho fixo com uma fila limitada e a política de
saturação caller-runs.
8.4.Usando um Semaphore para restringir a submissão de tarefas.
8.5. A interface ThreadFactory.
8.6. Fábrica de threads personalizada.
8.7. Classe base de threads personalizada.
8.8. Modificando um Executor criado com fábricas padrão.
8.9. Reservatório de threads estendido com registro de mensagem e de tempo.
8.10. Transformando uma execução seqüencial em uma execução paralela.
8.11. Transformando recursão terminal seqüencial em uma recursão paralela.
8.12. Esperando o resultado ser calculado em paralelo.
8.13. Abstração de quebra-cabeças como o quebra-cabeça de blocos deslizantes.
8.14. Nó de ligação para a infraestrutura de solucionador de quebra-cabeça.
8.16. Versão concorrente do solucionador de quebra-cabeças.
8.17. Trinco de resultado usado por ConcurrentPuzzleSolver.
8.18. Solucionador que reconhece quando não existe solução.
9.1. Implementando SwingUtilities usando um Executor.
9.3. Exemplo de listener de eventos.
9.4. Ligando uma tarefa de longa duração a um componente visual.
9.5. Tarefas de longa duração com resposta ao usuário.
9.6. Cancelando uma tarefa de longa duração.
9.7. Classe de tarefas de pano de fundo suportando cancelamento, notificação de finalização e
indicação de progresso.
9.8. Inicializando uma tarefa de longa duração e passível de cancelamento com BackgroundTask.
10.1. Deadlock por ordem de travamento simples. Não faça isso.
10.2. Deadlocks por ordem dinâmica de travamento. Não faça isso.
10.3. Induzindo uma ordenação às travas para evitar deadlocks.
10.4. Laço diretor que conduz ao deadlock sob condições típicas.
10.5. Deadlock por ordem de travamento entre objetos que cooperam entre si. Não faça isso.
10.6. Usando chamadas abertas para evitar deadlocks entre objetos cooperativos.
10.7. Trecho de uma descarga de thread após um deadlock.
11.1 Acesso serial a uma fila de tarefas.
11.2. Sincronização que não surte efeito. Não faça isso.
11.3. Candidato para elisão de trava.
11.4. Segurando uma trava por mais tempo do que o necessário.
11.5. Reduzindo a duração da trava.
11.6. Candidato para divisão de trava.
11.7. ServerStatus refatorado para usar travas divididas.
12.1 Buffer limitado usando Semaphore.
12.2. Teste unitário básico para BoundedBuffer.
12.3. Testando bloqueio e resposta a interrupção.
12.4. Gerador de números aleatórios de qualidade intermediária, adequado para testes.
12.5. Programa de teste produtor-consumidor para BoundedBuffer.
12.6. Classes produtora e consumidora usadas em PutTakeTest.
12.7. Testando vazamentos de recursos.
12.8. Fábrica de threads para testar ThreadPoolExecutor.
12.9. Método de teste que verifica a expansão do reservatório de threads.
12.10. Usando Thread.yield para gerar mais intercalações.
12.11. Temporizador baseado em barreira.
12.12. Testando com um temporizador baseado em barreira.
12.13. Programa diretor para TimedPutTest.
13.1. Interface Lock.
13.2. Protegendo o estado de um objeto usando ReentrantLock.
13.3. Evitando interbloqueios por ordem de travamento usando tryLock.
13.4. Travamento com limite de tempo.
13.5. Aquisição de trava passível de interrupção.
13.6. Interface ReadWriteLock.
13.7. Envolvendo um Map com uma trava de leitura-escrita.
14.1. Estrutura de ações dependentes de estado com bloqueio.
14.2. Classe base para implementações de buffer limitado.
14.3. Buffer limitado que pára abruptamente quando as pré-condições não são satisfeitas.
14.4. Lógica cliente para chamar GrumpyBoundedBuffer.
14.5. Buffer limitado usando bloqueio rudimentar.
14.6. Buffer limitado usando filas condicionais.
14.7. Forma canônica de métodos dependentes de estado.
14.8. Usando notificação condicional em BoundedBuffer.put.
14.9. Portão que fecha novamente usando wait e notifyAll.
14.10. Interface de Condition.
14.11. Buffer limitado usando variáveis de condição explícitas.
14.12.Semáforo contador implementado com Lock.
14.13. Formas canônicas para aquisição e liberação em AQS.
14.14. Trinco binário usando AbstractQueuedSynchronizer.
14.15. Implementação de tryAcquire do irregular ReentrantLock.
14.16. tryAcquireShared e tryReleaseShared de Semaphore.
15.1. Operação de CAS simulada.
15.2. Contador sem bloqueio usando CAS.
15.3. Preservando invariantes de múltiplas variáveis usando CAS.
15.4. Gerador de número aleatório usando ReentrantLock.
15.5. Gerador de número aleatório usando AtomicInteger.
15.6. Pilha sem bloqueio usando o algoritmo de Treiber (Treiber, 1986);
15.7. Inserção no algoritmo de fila sem bloqueio de Michael-Scott (Michael e Scott, 1996)
16.1. Programa com sincronização insuficiente que, surpreendentemente, funciona. Não faça isso.
16.2. Classe interna de FutureTask ilustrando o aproveitamento de sincronização.
16.3 Inicialização tardia sem segurança. Não faça isso.
16.4. Inicialização tardia segura em relação a threads.
16.5. Inicialização inteligente.
16.6. Dialeto de inicialização tardia de uma classe suporte.
16.7. Anti-padrão de travamento-de-checagem-dupla. Não faça isso.
16.8. Segurança de inicialização para objetos imutáveis.
XVII
Prefácio
No momento em que escrevo, processadores de múltiplos núcleos estão se tornando baratos o
suficiente para PCs medianos. Não por coincidência, muitas equipes de desenvolvedores estão notificando
cada vez mais relatórios de defeitos relacionados a thread em seus projetos. Em um comentário recente
no website de desenvolvedores do NetBeans, um dos principais mantenedores observou que uma única
classe havia sido modificada 14 vezes para corrigir problemas com threads. Dion Almaer, antigo editor
do TheServerSide, comentou recentemente em seu blog (após uma dolorosa sessão de depuração que
por fim revelou um defeito de thread) que a maioria dos programas Java estão tão entupidos de defeitos
de concorrência que eles funcionam apenas “por acidente”.
De fato, desenvolver, testar e depurar programas com múltiplas threads pode ser extremamente
difícil já que os defeitos relacionados à concorrência não se manifestam de maneira previsível. E quando
eles vêm à tona é geralmente a pior hora possível – em produção, suportando pesadas cargas de dados.
Um dos desafios de desenvolver programas concorrentes em Java é o desencontro entre
funcionalidades de concorrência oferecidas pela plataforma e como os desenvolvedores precisam pensar
na concorrência em seus programas. A linguagem oferece mecanismos de baixo nível como sincronização
e esperas condicionais (os waits, mas esses mecanismos precisam ser usados consistentemente para
implementar protocolos no nível de aplicação ou políticas. Sem tais políticas torna-se fácil criar programas
que compilam e parecem funcionar, embora estejam comprometidos. Muitos excelentes livros de
concorrência não atingem seu objetivo por focar excessivamente os mecanismos de baixo nível e APIs,
em vez de políticas e padrões no nível do design.
Java 5.0 é um grande passo à frente no desenvolvimento de aplicações concorrentes em Java,
pois fornece novos componentes de alto nível e componentes adicionais de baixo nível, o que torna
fácil para novatos e veteranos construir aplicações concorrentes. Os autores são membros primários
notórios do grupo de especialistas do JCP que criou tais facilidades. Adicionalmente, para descrever seu
comportamento e funcionalidades, apresentamos os padrões de projeto sobre os quais se ergueram e
antecipamos cenários de utilização que motivaram sua inclusão nas bibliotecas da plataforma.
Nosso objetivo é dar aos leitores um conjunto de regras de projeto e modelos mentais que facilitam
– e deixam mais divertida – a construção correta e performática de classes e aplicações em Java.
Esperamos que vocês aproveitem Concorrência em Java na Prática.
Brian Goetz
Williston, VT
Março 2006
XVIII
Prefácio
Como usar este livro
Para equilibrar as divergências entre os mecanismos de baixo nível do Java e as políticas no nível
do design necessárias, apresentamos um conjunto simplificado de regras para escrever programas
concorrentes. Especialistas podem olhar para essas regras e dizer “Hmmm, isso não é totalmente
verdade: a classe C é segura em relação às threads mesmo se ela violar a regra R.”. Embora seja possível
escrever programas corretos quebrando nossas regras, fazê-lo requer um profundo entendimento dos
detalhes de baixo nível do Modelo de Memória Java e nós queremos que os desenvolvedores sejam
capazes de escrever programas corretos sem ter que dominar esses detalhes. Seguidas consistentemente,
nossas regras simplificadas irão produzir programas concorrentes corretos e manuteníveis.
Assumimos que o leitor já possui alguma familiaridade com os mecanismos básicos de concorrência
em Java. Concorrência em Java na Prática não é uma introdução à concorrência – para isso veja o capítulo
de threads de qualquer volume introdutório decente, como o A linguagem de Programação Java (Arnold
et al.,2005). Tampouco é este uma referência enciclopédica para Todas as Coisas em Concorrência –
para isso veja Programação Concorrente em Java (Lea, 2000). De sua parte, este livro oferece regras práticas
de projeto para auxiliar desenvolvedores no difícil processo de criar classes seguras e performáticas
em relação às threads. Onde apropriado cruzamos referências de seções relevantes de A linguagem
de Programação Java, Programação Concorrente em Java, The Java Language Specification (A Especificação da
Linguagem Java – Gosling et al.,2005) e Effective Java (Java Eficaz – Bloch, 2001) usando as convenções
[JPL n.m] , [CPJ n.m], [JLS n.m] e [EJ item n].
Após a introdução (Capítulo 1), o livro é dividido em quatro partes:
Fundamentos. A Parte I (Capítulos 2-5) foca nos conceitos básicos de concorrência e segurança
em threads e como compor classes seguras threads com as ferramentas fornecidas na biblioteca de
classes. Uma planilha resumo sumarizando o ponto mais importante das regras apresentadas na Parte I
aparece na página XXX(original 110).
Os Capítulos 2 (Segurança em relacão às threads) e 3 (Compartilhando objetos) formam o alicerce
do livro. Praticamente todas as regras para evitar os perigos de concorrência, construir classes seguras
em relação às threads e verificar a segurança em threads estão aqui. Leitores que preferem a “prática”
à “teoria” podem sentir-se tentados a pular para a Parte II, mas voltem e ler os capítulos 2 e 3 antes de
escreverem código concorrente!
O Capítulo 4 (Compondo Objetos) cobre as técnicas para compor classes seguras em relação às
threads em outras iguais maiores. O Capítulo 5 (Ferramentas) cobre as ferramentas de concorrência –
coleções seguras em threads e sincronizadores – fornecidos nas bibliotecas da plataforma.
Estruturando Aplicações. A Parte II (Capítulos 6-9) descreve como explorar as threads para
melhorar o tempo de resposta ou a vazão de aplicações concorrentes. O Capítulo 6 (Execução de
Tarefas) cobre a identificação de tarefas paralelizáveis e sua execução no infra-estrutura de execução de
tarefas. O Capítulo 7 (Cancelamento e Desligamento) lida com as técnicas para finalizar tarefas e threads
antes do tempo; como programas lidam com cancelamento e desligamento, é geralmente um dos fatores
que separam aplicações concorrentes verdadeiramente robustas daquelas que apenas funcionam. O
Capítulo 8, Utilizando reservatórios de threads, endereça algumas das mais avançadas funcionalidades da
infra-estrutura de execução de tarefas. O Capítulo 9 (Aplicações GUI) foca nas técnicas para melhorar
o tempo de resposta em subsistemas de thread única.
Vida, Performance e Teste. A Parte II (Capítulos 10-12) preocupa-se em garantir que programas
concorrentes realmente façam o que você deseja que eles façam e com desempenho aceitável. O Capítulo
10 (Evitando Perigos de Ativação) descreve como evitar as falhas de ativação que podem impedir os
programas de prosseguir sua execução. O Capítulo 11 (Performance e Escalabilidade) cobre as técnicas
para melhorar o desempenho e a escalabilidade de código concorrente. O Capítulo 12 (Testando
Programas Concorrentes) fala das técnicas de teste do código concorrente em relação à performance.
Tópicos Avançados. A Parte IV (Capítulos 13-16) engloba os tópicos que, provavelmente,
interessam mais aos desenvolvedores experientes: travamentos (locks) explícitos, variáveis atômicas,
algoritmos sem bloqueio e o desenvolvimento de sincronizadores personalizados.
Prefácio
XIX
Exemplos de Código
Enquanto muitos dos conceitos gerais neste livro são aplicáveis a versões anteriores do Java 5.0
e mesmo a ambientes que não sejam Java, a maioria dos exemplos de código (e todas as afirmações
sobre o Modelo de Memória Java) assume Java 5.0 ou superior. Alguns exemplos podem mesmo usar
funcionalidades de bibliotecas adicionadas ao Java 6.
Os exemplos de código foram comprimidos para reduzir seu tamanho e destacar suas porções
relevantes. As versões completas dos códigos, bem como exemplos suplementares e erratas, estão
disponíveis no web site do livro original: http://www.javaconcurrencyinpractice.com.
Há três tipos de exemplos de código: “bons”, “não tão bons” e “ruins”. Exemplos bons ilustram
técnicas que devem ser reproduzidas. Exemplos ruins ilustram técnicas que, definitivamente, não devem
ser reproduzidas, são identificadas com um ícone do “Sr. Argh1” para tornar claro que é um código
“tóxico” (veja a listagem 1). Exemplos não tão bons ilustram o que não necessariamente está errado, mas
é frágil, arriscado ou de baixa performance e estão decorados como “Sr. Poderia Ser mais Feliz” como
na listagem 2.
public <T extends Comparable<? super T>> void sort(List<T> list) {
//Nunca retorna a resposta errada!
System.exit(0);
}
Listagem 1. Maneira ruim de ordenar uma lista. Não faça isso.
Alguns leitores podem questionar o papel dos exemplos “ruins” neste livro, afinal de contas, um
livro deveria mostrar como fazer certo e não errado. Os exemplos ruins têm dois propósitos. Eles
ilustram equívocos comuns, mas embora o importante seja demonstrar como analisar um programa
para segurança em threads - a melhor maneira de fazer isso é mostrar como essa segurança fica
comprometida.
public <T extends Comparable<? super T>> void sort(List<T> list) {
for (int i=0; i<1000000; i++)
doNothing();
Collections.sort(list);
}
Listagem 2. Maneira não tão boa de ordenar uma lista.
Agradecimentos
Esse livro nasceu do processo de desenvolvimento para o pacote java.util.concurrent, criado pela JSR
166 do JCP para inclusão no Java 5.0. Muitas outras pessoas contribuíram com a JSR 166; em particular
agradecemos a Martin Buchholz por fazer todo o trabalho de obter o código no JDK e a todos os leitores
da lista de discussão concurrency-interest que ofereceram sugestões e críticas nos rascunhos das APIs.
Este livro foi bastante aperfeiçoado com as sugestões, críticas e assistência de um pequeno exército
de revisores, conselheiros e chefes. Gostaríamos de agradecer a Dion Almaer, Tracy Bialik, Cincy
Bloch, Martin Buchholz, Paul Christmann, Cliff Click, Stuart Halloway, David Hovemeyer, Jason
Hunter, Jeremy Hylton, Heinz Kabutz, Robert Kuhar, Ramnivas Laddad, Jared Levi, Nicole Lewis,
Victor Luchangco, Jeremy Manson, Paul Martin, Berna Massingil, Michael Maurer, Ted Neward, Kikr
Pepperdine, Bill Pugh, Sam Pullara, Russ Ruffer, Bill Scherer, Jeffrey Siegal, Bruce Tate, Gil Tene, Paul
Tyma e membros do Silicon Valley Patterns Group que, durante muitas conversas técnicas interessantes
ofereceram auxílio e fizeram sugestões que ajudaram a tornar este livro melhor.
1 “No original: “Mr. Yuck” é marca registrada do Hospital Infantil de Pittsburgh e aparece com permissão.
XX
Prefácio
Somos especialmente gratos a Cliff Biffle, Barry hayes, David Kurzyniec, Angelika Langer, Doron
Rajwan e Bill Venners, que revisaram o manuscrito completo nos mínimos detalhes, encontraram
defeitos nos exemplos de código e sugeriram inúmeras melhorias.
Agradecemos a Katrina Avery por um grande trabalho de cópia-edição e Rosemary Simpson por
produzir o índice em um prazo irreal. Agradecemos a Amir Dewar por fazer as ilustrações.
Agradecemos a todo o time da Addison-Wesley que ajudou a fazer deste livro uma realidade. Ann
Sellers lançou o projeto e Greg Doench o completou com tranqüilidade; Elizabeth Ryan o guiou ao
processo de produção.
Gostaríamos de agradecer também aos milhares de engenheiros de software que contribuíram
indiretamente criando os softwares usados para criar este livro, incluindo TEX, LATEX, Adobe, Acrobat,
pic, grap, Adobe Illustrator, Perl, Apache, Ant, IntelliJ IDEA, GNU emacs, Subversion, TortoiseSVN e,
claro, a biblioteca de classes da plataforma Java.
Download