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.