SlideShare uma empresa Scribd logo
1 de 175
Baixar para ler offline
UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA
CURSO DE CIÊNCIA DA COMPUTAÇÃO
PROGRAMAÇÃO CONCORRENTE – 2015.1
Fábio M. Pereira
(fabio.mpereira@uesb.edu.br)
Roteiro
• Concorrência
• Objetos e Concorrência
– Construtores de Execução Concorrente
– Concorrência e Programação Orientada a Objetos
– Modelos de Objetos e Mapeamento
• Forças de Projeto
– Segurança
– Vivacidade
– Performance
– Reusabilidade
• Padrões de Projeto
– Aplicação de Camadas
– Adaptadores
– Subclasses
– Adaptadores de Método
Concorrência
• Informalmente, um programa concorrente é aquele que
faz mais de uma coisa ao mesmo tempo
• Por exemplo, um navegador web pode simultaneamente
realizar uma requisição HTTP GET para buscar uma
página, executar um clipe de áudio, mostrar a quantidade
de bytes recebidos de alguma imagem e realizar um
diálogo de advertência com o um usuário
• Mas às vezes esta simultaneidade é uma ilusão
– Em alguns sistemas computacionais estas atividades diferentes
podem ser realizadas por diferentes CPUs
– Em outros sistemas elas são realizadas por uma única CPU
compartilhada que troca entre diferentes atividades tão rápido
que elas parecem serem simultâneas para um observador
humano
Concorrência
• Uma definição de programação concorrente mais precisa,
porém não muito interessante, pode ser feita
operacionalmente:
– Uma máquina virtual Java e o sistema operacional fornecem
mapeamentos a partir de simultaneidade aparente para
paralelismo físico (via múltiplas CPUs) ou, na falta deste, por
permitir que atividades independentes sejam realizadas em
paralelo quando possível ou desejável, e em outro caso, por
compartilhamento de tempo
– Programação concorrente consiste no uso de construtores de
programação que são mapeados desta maneira
– Por convenção, esta noção está restrita a construtores afetando
uma única JVM, como oposto à programação distribuída, que,
por exemplo, usando remote method invocation (RMI), envolve
múltiplas JVMs residindo em múltiplos sistemas computacionais
Concorrência
• Concorrência e os motivos para seu uso são melhor
capturados quando consideramos a natureza de alguns
tipos comuns de aplicações concorrentes:
– Web services: a maioria dos web services baseados em sockets
(por exemplo, daemons HTTP, máquinas servlet e servidores de
aplicação) são multithreaded, normalmente, a principal
motivação e dar suporte a múltiplas conexões concorrentes
para garantir que uma nova solicitação de conexão não precise
aguardar a finalização de outras, o que geralmente minimiza a
latência e aumenta a disponibilidade
– Cálculos numéricos: muitas tarefas intensivas de computação
podem ser paralelizadas, e então serem executadas mais rápido
se múltiplas CPUs estão presentes, aqui o objetivo é maximizar
a saída explorando o paralelismo
Concorrência
• Continuação...
– Processamento I/O: mesmo em um computador nominalmente
sequencial, dispositivos que realizam leituras e escritas em
discos, redes, etc., operam independentemente da CPU,
programas concorrentes podem usar o tempo que outros
programas perdem esperando por I/O lento, e podem então
fazer uso mais eficiente do recursos de um computador
– Simulação: programas concorrentes podem simular objetos
físicos com comportamento autônomo independente que são
difíceis de realizar em programas puramente sequenciais
Concorrência
• Continuação...
– Aplicações baseadas em GUI: mesmo que a maioria das
interfaces do usuário sejam intencionalmente single-threaded,
elas geralmente estabelecem ou se comunicam com serviços
multithreaded, a concorrência permite que o controle do
usuário continue “responsivo” mesmo durante ações que
consomem muito tempo
– Software baseado em componentes: componentes de software
de alta granularidade (por exemplo, aqueles que fornecem
ferramentas de projeto, como editores de layout) pode
internamente construir threads de maneira a assistir no
registro, fornecer suporte multimídia, alcançar maior
autonomia ou melhorar a performance
Concorrência
• Continuação...
– Código móvel: frameworks como o pacote java.applet executam
código baixado em threads separadas como uma parte de um
conjunto de políticas que ajudam a isolar, monitorar e controlar
os efeitos de código desconhecido
– Sistemas embarcados: a maioria de programas executados em
pequenos dispositivos dedicados realizam controle de tempo
real, múltiplos componentes estão continuamente reagindo a
entradas externas de sensores e outros dispositivos e produzem
saídas externas de uma maneira ininterrupta. Todas as
implementações JVM dão suporte a controle de tempo real
leve, no qual linhas de tempo e performance são considerados
tópicos de qualidade de serviço em vez de corretude. Isto
reflete objetivos de portabilidade que permitem que a JVM seja
implementada em modernos sistemas de hardware e software
de multipropósitos
Construtores de Execução Concorrente
• Threads são apenas um dos vários construtores disponíveis
para execução de código concorrente
• A ideia de gerar uma nova atividade pode ser mapeada para
qualquer uma de várias abstrações que refletem trade-offs de
autonomia versus overhead
• Projetos baseados em threads nem sempre fornecem a
melhor solução para um determinado problema de
concorrência
• A escolha de uma das alternativas apresentadas a seguir
podem fornecer mais ou menos segurança, proteção,
tolerância a falhas e controle administrativo, assim como
maior ou menor overhead
• Diferenças entre estas opções (e seus construtores de
programação de suporte associados) impactam em diferentes
estratégias de projeto
Sistemas Computacionais
• Se tivéssemos uma grande oferta de sistemas
computacionais, poderíamos mapear cada unidade lógica
de execução para um computador diferente
• Cada sistema computacional pode ter um único
processador, um multiprocessador, ou um conjunto de
máquinas administradas como uma única unidade e
compartilhando um sistema operacional comum:
– Isso proporciona autonomia ilimitada e independência
– Cada sistema pode ser administrado separadamente e
controlado a partir de todos os outros
Sistemas Computacionais
• No entanto, a construção, localização, recuperação, e
passagem de mensagens entre tais entidades podem ser
caros, as oportunidades para compartilhamento de
recursos locais são eliminados, e soluções para os
problemas em torno de nomes, segurança, tolerância a
falhas, recuperação e acessibilidade são todos
relativamente pesados em comparação com os
observados em programas concorrentes
• Portanto, esta escolha de mapeamento é tipicamente
aplicado apenas em relação aos aspectos de um sistema
que exige uma solução intrinsecamente distribuída
• E mesmo dispositivos embarcados de computador
ínfimos hospedam mais de um processo
Processos
• Um processo é uma abstração do sistema operacional
que permite que um sistema computacional dê suporte a
muitas unidades de execução
• Cada processo representa tipicamente um programa em
execução em separado; por exemplo, uma execução de
JVM
• Como a noção de um "sistema computacional", um
"processo" é uma abstração lógica, não física, assim, por
exemplo, ligações de processos para CPUs podem variar
dinamicamente
Processos
• Os sistemas operacionais garantem algum grau de
independência, a ausência de interferências e segurança
entre os processos executados concorrentemente
• Processos em geral, não têm permissão para acessar locais de
armazenamento uns dos outros (embora geralmente existam
algumas exceções), e devem, em vez disso, se comunicar via
recursos de comunicação entre processos, tais como pipes
(tubos)
• A maioria dos sistemas fazem pelo menos promessas de
melhor esforço sobre como processos serão criados e
agendados
• Isso quase sempre implica em compartilhamento de tempo
preemptivo - a suspensão de processos em uma base
periódica para dar a outros processos a chance de executar
Processos
• A sobrecarga para a criação, gestão e comunicação entre
processos pode ser muito menor do que em soluções por
máquina
• No entanto, uma vez que os processos compartilham
recursos computacionais subjacentes (CPUs, memória,
canais de E/S), eles são menos autônomos
– Por exemplo, um travamento de máquina causado por um
processo mata todos os processos
Threads
• Construtores de threads de várias formas fazem mais
trade-offs em autonomia, em parte por causa da menor
sobrecarga
• Os principais trade-offs são:
– Compartilhamento
– Agendamento
– Comunicação
Threads
call
call
task
task
thread
call
call
task thread
process
systemcall
call
call
call
task
task
thread process
call
call
task
task
thread
thread
process
Compartilhamento
• Threads podem compartilhar o acesso à memória,
arquivos abertos e outros recursos associados a um único
processo
• Threads na linguagem de programação Java podem
compartilhar todos esses recursos
• Alguns sistemas operacionais também suportam
construções intermediárias, por exemplo "processos
leves" e "threads do kernel" que compartilham apenas
alguns recursos, fazendo-o apenas mediante pedido
explícito, ou impondo outras restrições
Agendamento
• Garantia de independência pode ser enfraquecida para apoiar
políticas de escalonamento mais baratas
• Em um extremo, todas as threads podem ser tratadas em
conjunto, como um processo single-threaded, caso em que
elas podem lidar de forma cooperativa com as outras, de
modo que apenas uma thread está sendo executada de cada
vez, sem dar a qualquer outra thread a chance de executar
até que ela bloqueie
• No outro extremo, o agendador subjacente pode permitir que
todas as threads em um sistema lidem diretamente umas com
as outras através de regras de agendamento preemptivas
• Threads na linguagem de programação Java podem ser
agendadas usando qualquer política que encontra-se em
qualquer lugar entre estes dois extremos
Comunicação
• Sistemas interagem via comunicação através de canais com
ou sem fios, por exemplo, utilizando sockets
• Processos também podem se comunicar dessa forma, mas
também podem utilizar mecanismos mais leves, como pipes
(tubos) e recursos de sinalização entre processos
• Threads podem usar todas essas opções, além de outras
estratégias mais baratas que dependem de acesso a posições
de memória acessíveis por múltiplas threads, e empregando
recursos de sincronização baseadas em memória, como
bloqueios, espera e os mecanismos de notificação
• Estes construtores apoiam a comunicação de maneira mais
eficiente, mas algumas vezes incorrem em maior
complexidade e, consequentemente, maior potencial de erro
de programação
Tarefas e Ambientes Leves de Execução
• Os trade-offs efetuados em suporte a threads cobrem
uma ampla gama de aplicações, mas que nem sempre
são perfeitamente compatíveis com as necessidades de
uma determinada atividade
• Embora os detalhes de desempenho diferem em todas as
plataformas, a sobrecarga na criação de uma thread
ainda é significativamente maior do que a forma mais
barata (mas menos independente) para invocar um bloco
de código – chamando-o diretamente na thread atual
Tarefas e Ambientes Leves de Execução
• Quando a sobrecarga de criação e gerenciamento de
threads tornam-se preocupações de desempenho,
podemos ser capazes de fazer concessões adicionais de
autonomia pela criação de nossos próprios ambientes de
execução mais leves, que impõem novas restrições à sua
utilização (por exemplo, proibindo o uso de certas formas
de bloqueio), ou fazendo menos garantias de
agendamento, ou restringindo a sincronização e
comunicação a um conjunto mais limitado de opções
Tarefas e Ambientes Leves de Execução
• Os ambientes leves de execução mais familiares são
sistemas baseados em eventos e subsistemas, nos quais
as chamadas disparadas de atividades conceitualmente
assíncronas são mantidas como eventos que podem ser
enfileirados e processados ​​por threads em background
• Quando se aplicam, a construção e utilização de tais
ambientes podem melhorar tanto a estrutura como o
desempenho de programas concorrentes
• O seu uso reduz as preocupações que podem de outro
modo inibir o uso de técnicas de execução concorrente
para expressar atividades logicamente assíncronas e
objetos logicamente autônomos
Concorrência e Programação OO
• Objetos e concorrência estão vinculados, desde os
primeiros dias de cada um
• A primeira linguagem de programação concorrente OO
(criada em cerca de 1966), Simula, também foi a primeira
linguagem OO, e foi uma das primeiras linguagens
concorrentes
• Os construtores iniciais de OO e concorrência de Simula
eram um tanto primitivos e estranhos
– Por exemplo, a concorrência foi baseado em torno de co-rotinas
– construtores baseados em thread que exigiam que
programadores explicitamente passassem o controle de uma
tarefa para outra
Concorrência e Programação OO
• Várias outras linguagens que oferecem ambos os
construtores, de concorrência e OO, se seguiram – na
verdade, até mesmo algumas das versões dos primeiros
protótipos de C++ incluíam algumas classes de bibliotecas
com suporte à concorrência
• E Ada (embora, em suas primeiras versões, dificilmente
se parecia com uma linguagem OO) ajudou a trazer
programação concorrente para fora do mundo dos
sistemas e linguagens de baixo nível especializados
Concorrência e Programação OO
• Projeto OO não desempenhou nenhum papel prático nos
sistemas multithreaded de programação emergentes na
década de 1970
• E a concorrência não desempenhou nenhum papel prático na
programação OO de larga escala que começou na década de
1980
• Mas o interesse em OO concorrente permaneceu vivo em
laboratórios de pesquisa e grupos de desenvolvimento
avançados, e ressurgiu como um aspecto essencial da
programação, em parte devido à popularidade e onipresença
da plataforma Java
• Programação OO concorrente compartilha a maioria das
características da programação de qualquer tipo, mas difere
em aspectos críticos dos tipos de programação que podemos
estar mais familiarizados
Programação OO Sequencial
• Programas OO concorrentes são muitas vezes
estruturados utilizando as mesmas técnicas de
programação e padrões de projeto como programas OO
sequenciais
• Mas eles são intrinsecamente mais complexos – quando
mais do que uma atividade pode ocorrer ao mesmo
tempo, a execução do programa é, necessariamente,
não-determinística
• O código pode executar em ordem surpreendente –
qualquer fim que não seja explicitamente excluído é
permitido, então não podemos sempre entender
programas concorrentes por leitura sequencial através de
seu código
Programação OO Sequencial
• Por exemplo, sem maiores precauções, um campo
definido para um valor em uma linha de código pode ter
um valor diferente (devido às ações de alguma outra
atividade simultânea) pelo tempo em que a próxima linha
de código é executada
• Lidar com essa e outras formas de interferência muitas
vezes introduz a necessidade de um pouco mais de rigor
e uma perspectiva mais conservadora no design
Programação Baseada em Eventos
• Algumas técnicas de programação concorrente têm
muito em comum com as de ambientes de eventos
empregadas em kits de ferramentas GUI como
o java.awt, javax.swing e em outras linguagens,
como Tcl/Tk e Visual Basic
• Em ambientes de GUI, eventos como cliques do mouse
são encapsulados como objetos Event que são
colocados em um único EventQueue
• Estes eventos são depois enviados e processados ​​um a
um em um único ciclo de eventos, que normalmente é
executado como uma thread separada
Programação Baseada em Eventos
• Este projeto pode ser estendido para dar suporte a
concorrência adicional por (entre outras táticas) a criação
de várias threads de ciclo de eventos, cada evento
processamento concorrentemente, ou até mesmo
disparando cada evento em uma thread separada
• Novamente, isso abre novas possibilidades de projeto,
mas também introduz novas preocupações sobre
interferência e coordenação entre as atividades
concorrentes
Programação de Sistemas Concorrentes
• Programação concorrente orientada a objetos difere de
programação de sistemas multithreaded em linguagens
como C, devido, principalmente, ao encapsulamento,
modularidade, extensibilidade, segurança e recursos de
segurança que faltam em C
• Além disso, suporte a concorrência é construído na
linguagem de programação Java, e não fornecido por
bibliotecas
• Isso elimina a possibilidade de alguns erros comuns, e
também permite que os compiladores automaticamente
e com segurança executem algumas otimizações que
precisam ser executadas manualmente em C
Outras Linguagens de Programação
Concorrente
• Essencialmente, todas as linguagens de programação
concorrente são, em algum nível, equivalentes, se
considerarmos apenas o sentido de todas as linguagens
concorrentes, acreditamos não termos definido as
características de concorrência certas
• No entanto, não é tão difícil fazer programas em uma
linguagem parecerem quase equivalentes aos de outras
linguagens ou aquelas que utilizam outros construtores,
através do desenvolvimento de pacotes, classes,
utilitários, ferramentas e convenções de codificação que
imitam recursos incorporados em outras
Modelos de Objetos e Mapeamentos
• Concepções de objetos muitas vezes diferem entre
programação OO sequencial e concorrente, e mesmo
entre diferentes estilos de programação OO concorrente
• Contemplação dos modelos de objetos e mapeamentos
subjacentes pode revelar a natureza das diferenças entre
os estilos de programação sugeridos na seção anterior
• A maioria das pessoas gosta de pensar em objetos de
software como modelos de objetos reais, representados
com algum grau arbitrário de precisão
• A noção de "real" é, naturalmente, aos olhos de quem vê,
e muitas vezes inclui artifícios que só fazem sentido
dentro do reino da computação
Exemplo
• Considerando o diagrama de classes UML e o esboço de
código para a classe WaterTank:
class Watertank {// esboço de código
final float capacity;
float currentVolume = 0.0f;
WaterTank overflow;
WaterTank(float cap) { capacity = cap; ... }
void addWater(float amount)
throws OverflowException;
void removeWater(float amount)
throws UnderflowException;
}
Exemplo
• A intenção aqui é representar, ou simular, um tanque de
água com:
– Atributos, tais como capacity e currentVolume, que são
representados como campos de objetos Watertank
– Podemos escolher somente os atributos que nos preocupemos
em algum contexto de uso, por exemplo, enquanto todos os
tanques de água reais têm locais, formas, cores e assim por
diante, esta classe só lida com volumes
– Restrições de estado invariantes, como os fatos de que
o currentVolume permanece sempre entre zero e capacity, e
que capacity não seja negativo e nunca mude após a
construção
Exemplo
– Operações que descrevem comportamentos, como aqueles
para addWater e removeWater
– Esta escolha de operações reflete novamente algumas decisões de design
implícitas relativas a exatidão, granularidade e precisão
– Por exemplo, poderíamos ter escolhido para modelar os reservatórios de
água ao nível das válvulas e interruptores, e poderíamos ter modelado cada
molécula de água como um objeto que muda de localização como o
resultado das operações associadas
Exemplo
– Conexões (e conexões potenciais) para outros objetos com os quais
objetos se comunicam, tais como tubos ou outros tanques
– Por exemplo, o excesso de água encontrado numa
operação addWater pode ser desviado para um tanque de
transbordamento que é conhecido por cada tanque
– Pré-condições e pós-condições sobre os efeitos das operações, como
as regras segundo as quais é impossível remover a água de um
tanque vazio, ou adicionar água para um tanque cheio que não esteja
equipado com um tanque de transbordamento disponível
– Restrições de Protocolos quando e como mensagens (os pedidos de
operação) são processados
– Por exemplo, podemos impor uma regra de que no máximo uma
mensagem de addWater ou removeWater é processada em um
determinado momento ou, em alternativa, uma regra afirmando que
as mensagens removeWater são permitidos no meio de
operações addWater
Modelos de Objetos
• A classe Watertank usa objetos para modelar a realidade
• Os modelos de objetos fornecem regras e ambientes para
a definição de objetos de forma mais geral, que abrange:
– Estado – a estrutura de cada objeto é descrito (normalmente
através de uma classe), em termos de atributos internos
(estado), conexões com outros objetos, métodos locais
(internos), e métodos ou portas para aceitar mensagens de
outros objetos
– Encapsulamento - os objetos têm membranas que separam o
seu interior do exterior, o estado interno pode ser diretamente
modificado apenas pelo próprio objeto
Modelos de Objetos
– Comunicação - objetos se comunicam apenas através de
passagem de mensagem, mensagens de objetos disparam
ações em outros objetos
– As formas dessas mensagens podem variar de chamadas de
procedimentos simples até aqueles transportados via
protocolos arbitrários de comunicação
– Identidade - novos objetos podem ser construídos em qualquer
momento (sujeito a restrições de recursos do sistema) por
qualquer objeto (sujeito a controle de acesso), uma vez
construído, cada objeto mantém uma identidade única que
persiste ao longo de sua vida
Modelos de Objetos
– Conexões – um objeto pode enviar mensagens para os outros,
se ele conhece suas identidades
– Alguns modelos contam com identidades de canal em vez de ou
em adição a identidades de objetos
– Abstratamente, um canal é um veículo para a passagem de
mensagens
– Dois objetos que compartilham um canal podem passar
mensagens através desse canal, sem saber as identidades de
cada um
– Linguagens e modelos OO típicos se baseiam em primitivas de
objeto para chamada direta de métodos, abstrações baseadas
em canal para ES e comunicação através de rede, e construções,
tais como canais de eventos que podem ser vistos a partir de
qualquer perspectiva
Modelos de Objetos
– Computação - objetos podem realizar quatro tipos básicos de
computação:
• Aceitar uma mensagem
• Atualizar estado interno
• Enviar uma mensagem
• Criar um novo objeto
• Esta caracterização abstrata pode ser interpretada e
refinada de várias maneiras:
– Por exemplo, uma forma de implementar um objeto
Watertank é a construção de um pequeno dispositivo de
hardware de propósito especial que só mantém os estados
indicados, instruções e conexões
– Mas desde que este não é um curso sobre design de hardware,
vamos ignorar tais opções e restringir a atenção para
alternativas baseadas em software
Mapeamento Sequencial
• As características de um computador comum de uso geral (CPU,
um canal de comunicação, alguma memória, e algumas portas IO)
podem ser exploradas de modo a que este computador pode fingir
que é qualquer objeto, por exemplo, um Watertank
• Isso pode ser realizado ao carregar uma descrição do reservatórios
de água (através de um arquivo .class) em uma JVM
• A JVM pode, então, construir uma representação passiva de uma
instância e, em seguida, interpretar as operações associadas
• Esta estratégia de mapeamento também se aplica ao nível da CPU
quando as operações são compilados em código nativo em vez de
interpretadas como bytecodes
• Ela também se estende aos programas que incluam muitos objetos
de classes diferentes, cada um carregado e instanciado, conforme
necessário, fazendo com que a JVM em todos os momentos
registre a identidade ("this") do objeto que está simulando
Mapeamento Sequencial
JVM Sequencial
Interpretador
interpret() {
...
}
IO
Representação
de um objeto
WaterTank Representação
de uma classe
WaterTank
Estado: contador do programa, endereços de objetos
Mapeamento Sequencial
• Em outras palavras, a JVM é em si um objeto, embora um
muito especial que pode fingir que é outro objeto (mais
formalmente, ela serve como uma Máquina Universal de
Turing)
• Enquanto estados semelhantes valem para os
mapeamentos utilizados na maioria das outras
linguagens, objetos Class e reflexão tornam mais
simples caracterizar objetos reflexivos que tratam outros
objetos como dados
• Em um ambiente puramente sequencial, isto seria o fim
da história
Mapeamento Sequencial
• Mas considerando as restrições sobre o modelo de
objetos genérico imposto por este mapeamento, em uma
JVM sequencial seria impossível simular diretamente
múltiplos objetos waterTank interagindo
concorrentemente
• E uma vez que toda passagem de mensagem é realizada
via invocação de procedimento sequencial, não há
necessidade de regras sobre se várias mensagens podem
ser processadas concorrentemente
• Assim, o processamento OO sequencial limita os tipos de
conceitos de projeto de alto nível que são permitidos
expressar
Objetos Ativos
• No outro extremo do espectro de mapeamento estão
modelos de objetos ativos (também conhecidos como
modelos de ator), em que cada objeto é autônomo
• Cada um pode ser tão poderoso como uma JVM
sequencial
• Classes internas e representações de objetos podem ter
as mesmas formas como as usadas ​​em estruturas
passivas
– Por exemplo, aqui, cada waterTank poderia ser mapeado para
um objeto ativo separado, pelo carregamento em uma
descrição de uma JVM separada, e, em seguida, sempre
permitindo simular as ações definidas
Objetos Ativos
• Modelos de objeto ativos formam uma visão de alto nível
comum de objetos em sistemas orientados a objetos
distribuídos: diferentes objetos podem residir em máquinas
diferentes, por isso o local e domínio administrativo de um
objeto são muitas vezes questões importantes de
programação
• Toda a passagem de mensagens é organizada através de
comunicação remota (por exemplo, através de sockets) que
podem obedecer a qualquer de uma série de protocolos,
incluindo:
– mensagens de sentido único (mensagens que não intrinsecamente
requerem respostas)
– multicasts (envio simultaneamente da mesma mensagem para vários
destinatários) e
– estilo procedimental de trocas de solicitação-resposta
Objetos Ativos
• Este modelo também serve como uma visão orientada a
objetos da maioria dos processos de nível do
sistema operacional, cada um dos quais é tão
independente de (e compartilham poucos recursos com)
outros processos quanto possível
Objetos Ativos
um objeto ativo
atributos, conexões
accept
message
anAction() {
update state
send messages
create objects
}
mensagem
Modelos Mistos
• Os modelos e mapeamentos subjacentes dão suporte a
concorrência na linguagem de programação Java e está
entre os dois extremos: dos modelos passivos e ativos
• Uma JVM completa pode ser composta de vários threads,
cada uma das quais atua da mesma maneira que uma
única JVM sequencial, no entanto, ao contrário dos
objetos ativos puros, todos essas threads podem
compartilhar o acesso ao mesmo conjunto de
representações passivas subjacentes
• Este estilo de mapeamento pode simular cada um dos
extremos
Modelos Mistos
• Modelos puramente sequenciais passivos podem ser
programados usando apenas uma única thread
• Modelos puramente ativos podem ser programados
através da criação de tantas threads quanto objetos
ativos existentes, evitando situações em que mais de
uma thread possa acessar uma determinada
representação passiva, e usando construtores que
fornecem os mesmos efeitos semânticos que passagem
de mensagens remotas, no entanto, a maioria dos
programas concorrentes ocupam uma posição
intermediária
Modelos Mistos
• Modelos OO concorrentes baseados em threads
conceitualmente separam objetos passivos "normais" de
objetos ativos (threads)
• Mas os objetos passivos geralmente exibem consciência-
thread não vista na programação sequencial, por
exemplo, pela proteção através de bloqueios
• E os objetos ativos são mais simples do que os
observados em modelos de ator, dando suporte a apenas
algumas operações (como run)
Modelos Mistos
• Mas o projeto de sistemas OO concorrentes pode ser
abordado a partir de qualquer uma destas duas direções:
– por objetos passivos conscientizados de existirem em um
ambiente multithread,
– ou por “emburrecimento” de objetos ativos para que eles
possam ser expressos mais facilmente usando construtores de
thread
Modelos Mistos
uma Thread
run() {
...
}
estado da thread
uma Thread
estado da thread
run() {
...
}
uma Thread
estado da thread
run() {
...
}
Representação
de um objeto
WaterTank Representação
de uma classe
WaterTank
Modelos Mistos
• Uma das razões para apoiar este tipo de modelo de
objetos é que ele mapeia de forma simples e eficiente o
armazenamento em hardware e sistemas operacionais
em processador único e em multi-processamento de
memória compartilhada (SMP):
– threads podem ser vinculadas a CPUs quando possível e
desejável, e de outra forma a tempo-compartilhado
– o estado de threads locais são mapeadas para registradores e
CPUs
– e representações de objetos compartilhados mapeados para a
memória principal compartilhada
Modelos Mistos
CPU
cache de
registradores
CPU
cache de
registradores
bus
IO
memória principal
uma representação
de objeto
Modelos Mistos
• O grau de controle do programador sobre esses
mapeamentos é uma distinção que separa muitas formas
de programação paralela de programação concorrente
• Programação paralela clássica envolve etapas de projeto
explícito para mapear threads, tarefas ou processos, bem
como de dados, para processadores físicos e seus locais
de armazenamento
• Programação concorrente deixa a maioria das decisões
de mapeamento para a JVM (e o sistema operacional
subjacente), isto melhora a portabilidade, às custas da
necessidade de acomodar as diferenças na qualidade de
implementação destes mapeamentos
Modelos Mistos
• Compartilhamento de tempo é realizado mediante a
aplicação do mesmo tipo de estratégia de mapeamento
para as próprias threads: representações de
objetos Thread são mantidas, e um agendador
organiza trocas de contexto em que o estado da CPU
correspondente a uma thread é guardado em sua
representação de armazenamento associada e
restaurado a partir de outra
• Vários refinamentos e extensões de tais modelos e
mapeamentos são possíveis
– Por exemplo, as aplicações e os sistemas de
objetos persistentes normalmente contam com bases de dados
para manter representações de objetos em vez de depender
diretamente na memória principal
Forças de Projeto
• Investigaremos preocupações de design que surgem no
desenvolvimento de software concorrente
• A maioria das construções e padrões de projeto
apresentadas mais adiante incluem descrições de como
eles resolvem forças aplicáveis ​​discutidas aqui (assim
como outras que são menos diretamente vinculadas à
concorrência, como a precisão, a realização de testes e
assim por diante)
Forças de Projeto
• Podemos tomar duas visões complementares de
qualquer sistema OO, centrada em objeto e centrada em
atividade:
Sistema = Objetos + Atividades
• Sob uma visão centrada em objeto, um sistema é uma
coleção de objetos interligados, mas é uma coleção
estruturada, não uma sopa de objetos aleatórios
• Objetos se aglomeram em grupos, por exemplo, o grupo
de objetos compreendendo uma ParticleApplet,
formando assim componentes e subsistemas maiores
Forças de Projeto
• Sob uma visão centrada em atividade, um sistema é um
conjunto de atividades possivelmente simultâneas
• Em um nível mais refinado, estes são apenas mensagens
individuais enviadas (normalmente, chamadas de
métodos)
• Eles, por sua vez se organizam em conjuntos de cadeias
de chamadas, sequências de eventos, tarefas, sessões,
transações e threads
• Uma atividade lógica, como executar o ParticleApplet,
pode envolver muitas threads
• Em um nível superior, algumas dessas atividades
representam casos de uso de todo o sistema
Forças de Projeto
• Nenhuma visão sozinha fornece uma imagem completa
de um sistema, uma vez que um determinado objeto
pode estar envolvido em várias atividades, e,
inversamente, uma determinada atividade pode se
estender por vários objetos
• No entanto, essas duas visões dão origem a dois
conjuntos complementares de preocupações com
correção, uma centrada em objeto e outra centrada em
atividade:
– Segurança - nada de ruim acontece a um objeto
– Vivacidade - algo eventualmente acontece dentro de uma
atividade
Forças de Projeto
• Falhas de segurança levam a um comportamento não
intencional em tempo de execução - as coisas começam a
acontecer errado
• Falhas em vivacidade podem levar a nenhum
comportamento - as coisas param de acontecer
• Infelizmente, algumas das coisas mais simples que
podemos fazer para melhorar as propriedades de
vivacidade podem destruir propriedades de segurança, e
vice-versa
• Torná-los ao mesmo tempo corretos pode ser um desafio
• Devemos equilibrar os efeitos relativos dos diferentes
tipos de falha em nossos programas
Forças de Projeto
• Mas é uma engenharia padrão (não apenas de
engenharia de software) a prática de colocar ênfase no
projeto preliminar sobre a segurança
• Quanto mais o seu código realmente importa, o melhor é
garantir que um programa não faça nada que possa levar
a um comportamento aleatório, até mesmo perigoso
• Por outro lado, a maioria do tempo gasto em ajustes em
projetos de concorrência na prática geralmente envolve
questões de vivacidade e de eficiência relacionadas à
vivacidade
Forças de Projeto
• E as vezes há boas razões para sacrificar seletivamente a
segurança pela vivacidade
– Por exemplo, pode ser aceitável para apresentações visuais
mostrar transitoriamente um total absurdo de execuções
concorrentes descoordenadas - desenhar pixels isolados,
indicadores incorretos de progresso ou imagens que não têm
relação com suas formas intencionais - se estivermos confiantes
de que este estado de coisas vai logo ser corrigido
Forças de Projeto
• Questões de segurança e vivacidade poderão ser
prorrogadas para abranger duas categorias de
preocupações de qualidade, uma principalmente
centrada em objeto e outra principalmente centrada em
vivacidade, que também estão, por vezes, em oposição
direta:
– Reutilização - a utilidade de objetos e classes em múltiplos
contextos
– Performance - o grau em que as atividades executam logo e
rapidamente
Segurança
• Práticas de programação concorrente seguras são
generalizações de práticas de programação sequenciais
seguras e protegidas
• Segurança em projetos concorrentes acrescenta uma
dimensão temporal às noções comuns de segurança de tipo
• Um programa com checagem de tipo pode não estar correto,
mas pelo menos ele não faz coisas perigosas como interpretar
mal os bits que representam um float como se fossem uma
referência de objeto
• Da mesma forma, um projeto concorrente seguro pode não
ter o efeito pretendido, mas pelo menos ele nunca encontra
erros devido à corrupção das representações, por conter
threads
Segurança
• Uma diferença prática entre segurança de tipo e
segurança multithreaded é que a maioria dos tópicos de
segurança de tipo podem ser verificados
automaticamente por compiladores
• Um programa que não consegue passar pela checagem
em tempo de compilação não pode sequer ser executado
• A maioria das questões de segurança multithreaded, no
entanto, não podem ser verificadas automaticamente, e
por isso deve contar com a disciplina do programador
• As técnicas para garantir a segurança descritas aqui
dependem de práticas cuidadosas de engenharia
(incluindo vários com raízes em formalismos) em vez de
os próprios métodos formais
Segurança
• Segurança multithreaded também adiciona uma dimensão
temporal para o projeto e técnicas de programação em torno
da segurança
• Práticas de programação segura desabilitam o acesso a
determinadas operações nos objetos e recursos a partir de
certos invocadores ou aplicações
• Controle de concorrência introduz desativação temporária de
acesso com base no exame das ações em curso realizadas por
outras threads
• O principal objetivo na preservação da segurança é garantir
que todos os objetos em um sistema mantenham estados
consistentes: estados em que todos os campos, e todos os
campos de outros objetos dos quais dependem, possuam
valores legais, significativos
Segurança
• Às vezes preciso muito trabalho para determinar exatamente
o que "legal" e o que "significativo" representam em uma
classe particular
• Um caminho é primeiro estabelecer invariantes de nível
conceitual, por exemplo, a regra de que os volumes de
tanques de água devem estar sempre entre zero e as suas
capacidades
• Estas geralmente podem ser reformulados em termos de
relações entre os valores de campos nas classes concretas
associadas
• Um objeto é consistente se todos os campos obedecem seus
invariantes
• Cada método público em cada classe deve levar um objeto de
um estado consistente para outro
Segurança
• Objetos seguros podem ocasionalmente entrar em
estados transitoriamente inconsistentes no meio de
métodos, mas nunca tentar iniciar novas ações quando
estão em estados inconsistentes
• Se cada objeto é projetado para executar ações somente
quando é logicamente capaz de fazê-lo, e se toda a
mecânica está devidamente implementada, então
podemos ter certeza de que um aplicativo usando esses
objetos não vai encontrar quaisquer erros devido a
inconsistência do objeto
Segurança
• Uma das razões para ser mais cuidadoso sobre invariantes em
programas concorrentes é a de que é muito mais fácil quebrá-
las inadvertidamente que na maioria dos programas
sequenciais
• A necessidade de proteção contra os efeitos da inconsistência
surge mesmo em contextos sequenciais, por exemplo, quando
processando exceções e retornos de chamada e ao fazer auto-
chamadas a partir de um método em uma classe para outro,
no entanto, estas questões tornam-se muito mais centrais em
programas concorrentes
• As formas mais comuns para garantir consistência empregam
técnicas de exclusão para garantir a atomicidade de ações
públicas – que cada ação seja executada até o fim, sem
interferência de outros
Segurança
• Sem essa proteção, inconsistências em programas
concorrentes podem resultar de condições de corrida
produzindo conflitos de armazenamento no nível de
células de memória:
– Conflitos de leitura/gravação - uma thread lê um valor de um
campo enquanto outra escreve nele, o valor visto pela thread
de leitura é difícil de prever, já que depende de qual thread
ganhou a "corrida" para acessar o campo pela primeira vez, o
valor lido não precisa nem mesmo ser um valor que já foi
escrito por qualquer thread
– Conflitos escrita/escrita - duas threads tentam escrever no
mesmo campo, o valor visto na próxima leitura é novamente
difícil ou impossível de prever
Segurança
• É igualmente impossível prever as consequências das
ações que são tentadas quando os objetos estão em
estados inconsistentes
• Os exemplos incluem:
– Uma representação gráfica (por exemplo de uma partícula) é
apresentada num local que o objeto nunca efetivamente
ocupou
– Um saldo de conta bancária está incorreto após uma tentativa
de retirar o dinheiro no meio de uma transferência automática
– Seguir o próximo ponteiro de uma lista ligada leva a um nó que
ainda não está na lista
– Duas atualizações simultâneas de sensores fazem com que um
controlador de tempo real realize uma ação com efeito
incorreto
Atributos e Restrições
• Técnicas de programação segura confiam na
compreensão clara das propriedades e restrições
necessárias que cercam representações de objetos
• Os desenvolvedores que não estão cientes dessas
propriedades raramente fazem um trabalho muito bom
em preservá-los
• Muitos formalismos estão disponíveis para precisamente
representar predicados que descrevem requisitos
– Estes podem ser muito úteis, mas aqui vamos manter a precisão
suficiente sem a introdução de formalismos
Atributos e Restrições
• Requisitos de consistência, por vezes, resultam de
definições conceituais de alto nível de atributos
realizadas durante o projeto inicial de classes
• Estas restrições normalmente existem
independentemente de como os atributos são
concretamente representados e acessados via campos e
métodos
• Isto foi visto, por exemplo, no desenvolvimento da classe
Watertank
Atributos e Restrições
• Aqui estão alguns outros exemplos:
– Uma ContaBancaria tem um saldo que é igual à soma de todos
os depósitos e retiradas menos juros e taxas de serviço
– Um Pacote tem um destino que deve ser um endereço IP legal
– Um Contador tem um valor de contagem integral não-negativo
– Uma Fatura tem um pagamento devido que reflete as regras
de um sistema de pagamento
– Um Termostato tem uma temperatura igual à leitura mais
recente do sensor
– Uma Figura tem uma localização, dimensão e cor que
obedecem um conjunto de diretrizes de estilo para um
determinado conjunto de ferramentas GUI
Atributos e Restrições
• Aqui estão alguns outros exemplos (continuação):
– Um BufferLimitado tem um contadorDeElementos que
está sempre entre zero e uma capacidade
– Uma Pilha tem um tamanho e, quando não estiver vazia, um
elemento de topo
– Uma Janela tem um conjuntoDePropriedades que mantém
mapeamentos atuais de fontes, cor de fundo, etc.
– Um Intervalo tem uma dataInicial que não seja posterior a
sua dataFinal
Atributos e Restrições
• Enquanto tais atributos essencialmente sempre de
alguma maneira são mapeados em campos de objetos, as
correspondências não precisam ser diretas
• Por exemplo, o topo de uma Pilha não é normalmente
armazenado em uma variável, mas, em vez disso em um
elemento de uma matriz ou nó de lista ligada
• Além disso, alguns atributos podem ser calculados
("derivados") através de outros, por exemplo, o atributo
booleano descoberto de uma ContaBancaria pode
ser calculado através da comparação do saldo com zero
Restrições Representacionais
• Outras restrições e invariantes que são feitas para uma
determinada classe normalmente surgem como decisões
de implementação adicionais
• Campos declarados em prol da manutenção de uma
estrutura de dados particular, para melhorar o
desempenho, ou para outros fins de controle interno,
muitas vezes precisam respeitar um conjuntos de
invariantes
Restrições Representacionais
• Campos e restrições podem ser classificados em diversas
categorias, como apresentado a seguir
• Representações de valor direto - os campos necessários para
implementar atributos concretos, por exemplo, um buffer
pode ter um campo putIndex mantendo a posição do índice
do array, usado para inserir o próximo elemento adicionado
• Representações de valor armazenado em cache - campos
usados ​​para eliminar ou minimizar a necessidade de cálculos
ou invocações de método, por exemplo, em vez de calcular o
valor de descoberto cada vez que for necessário, uma
ContaBancaria pode manter um campo descoberto
que é verdadeiro se, e somente se, o saldo atual é inferior a
zero
Restrições Representacionais
• Representações de estado lógico - reflexões do estado de
controle lógico, por exemplo, um BankCardReader
pode ter um campo cartão que representa o cartão
que está sendo lido, e um campo validPIN que
registra se o código de acesso PIN foi verificado, o campo
validPIN do CardReader pode ser usado para
controlar o ponto em um protocolo em que o cartão foi
lido com sucesso e então validado
– Algumas representações de estado assumem a forma de
variáveis ​​de regras, controlando respostas a todo um conjunto
de métodos relacionados (às vezes aquelas declaradas em uma
única interface), por exemplo, um objeto de jogo pode alternar
entre papéis ativo e passivo, dependendo do valor de um
campo whoseTurn
Restrições Representacionais
• Variáveis ​​de estado de execução - campos que registram o
estado dinâmico de um objeto, por exemplo, o fato de que
uma determinada operação está em andamento
– Variáveis ​​de estado de execução podem representar o fato de que
uma determinada mensagem foi recebida, que a ação
correspondente foi iniciada, que a ação tenha terminado e que uma
resposta à mensagem foi emitida
– Uma variável de estado de execução é muitas vezes um tipo
enumerado com os valores representando o estado, por exemplo,
CONECTANDO, ATUALIZANDO, EMESPERA
– Outro tipo comum de variável de estado de execução é um contador
que registra o número de entradas ou saídas de algum método
– Objetos em programas concorrentes tendem a requerer mais
variáveis ​​do que aqueles em contextos sequenciais, para ajudar a
controlar e gerenciar o progresso dos métodos que executam de
forma assíncrona
Restrições Representacionais
• Variáveis ​​de história - representações da história ou
estados passados ​​de um objeto
– A representação mais ampla é um log da história, registrando
todas as mensagens já recebidas e enviadas, juntamente com
todas as ações internas correspondentes a mudanças de estado
que tenham sido iniciadas ou concluídas
– Subconjuntos menos extensos são muito mais comuns, por
exemplo, uma classe BankAccount poderia manter um campo
lastSavedBalance que mantém o último valor de checkpoint e
poderia ser utilizado para reverter operações canceladas
Restrições Representacionais
• Variáveis ​​de controle de versão - um inteiro, marcador de
tempo, objeto de referência, código de assinatura, ou
outra representação indicando o tempo, ordem, ou a
natureza da última mudança de estado feito por um
objeto, por exemplo, um Termostato pode incrementar
um numeroDeLeitura ou gravar o
ultimoHorarioDeLeitura quando atualizar sua
Temperatura
• Referências a conhecidos - campos apontando para
outros objetos que o host interage, mas que não
restrigem-se ao estado lógico do host, por exemplo, um
alvo de callback de um EventDispatcher, ou um
requestHandler delegado por um WebServer
Restrições Representacionais
• Referências a objetos de representação - os atributos que
são conceitualmente mantidos por um objeto host, mas
que são, na verdade, gerenciados por outros objetos
auxiliares
– Campos de referência podem apontar para outros objetos que
auxiliam na representação do estado do objeto host
– Assim, o estado lógico de qualquer objeto pode incluir os
estados de objetos que ele possui referência
– Além disso, os próprios campos de referência fazem parte do
estado concreto do objeto host
– Todas as tentativas para garantir a segurança devem levar esses
relacionamentos em consideração
Restrições Representacionais
• Referências a objetos de representação (exemplos):
– Uma Pilha pode ter um campo headOfLinkedList para
manter o primeiro nó de uma lista que representa a pilha
– Um objeto Pessoa pode manter um campo homePageUrl
mantido como um objeto java.net.URL
– O saldo de um BankAccount pode ser mantido em um
repositório central, caso em que o BankAccount poderia
manter um campo com referência ao repositório (a fim de
consultar o saldo atual), neste caso, uma parte do estado lógico
do BankAccount na realidade é gerenciado pelo repositório
Vivacidade
• As preocupações de segurança devem ser equilibradas por
preocupações de vivacidade
• Algumas propriedades "liveness" podem ser construídas
como propriedades de segurança de conjuntos de objetos
thread
• Por exemplo, o deadlock-freedom pode ser definido evitando-
se o mau estado no qual um conjunto de threads
infinitamente esperam umas pelas outras
• Em sistemas “vivos”, todas as atividades eventualmente
progridem em direção à conclusão, cada método chamado
eventualmente é executado, mas uma atividade pode (talvez
apenas transitoriamente) não progredir por qualquer uma das
várias razões inter-relacionadas apresentadas a seguir
Vivacidade
• Bloqueio (locking) – um método sincronizado bloqueia uma
thread porque outra thread mantém o bloqueio
• Esperar (waiting) - um método bloqueia (via Object.wait
ou seus derivados) à espera de um evento, mensagem, ou
condição que ainda tem de ser produzida dentro de outra
thread
• Entrada (input)- um método baseado em IO espera por uma
entrada que ainda não chegou a partir de um outro processo
ou dispositivo
• Contenção de CPU - uma thread não consegue executar
mesmo que esteja em um estado executável porque outras
threads, ou mesmo programas completamente separados em
execução no mesmo computador, estão ocupando CPU ou
outros recursos computacionais
Vivacidade
• Falha (failure) - um método executando em uma thread
encontra uma exceção prematura, erro ou falha
• Bloqueios momentâneos em threads em progresso são
geralmente aceitáveis, na verdade, bloqueios frequentes
de curta duração é intrínseco a muitos estilos de
programação concorrente
• O ciclo de vida típico de uma thread pode incluir um
número de bloqueios transientes e reescalonamentos, no
entanto, a falta permanente ou ilimitada de progresso é
geralmente um problema sério
Ciclo de Vida Típico de uma Thread
Criada
Pronta
para
execução
Em
execução
Bloqueada
Concluída
início
agendamento
bloqueiodesbloqueio
retorno, falha
Vivacidade
• Exemplos de falhas liveness potencialmente permanentes
incluem:
– Impasse (deadlock) - dependências circulares entre bloqueios,
no caso mais comum, thread A mantém um bloqueio para o
objeto X e, em seguida, tenta conseguir um bloqueio para o
objeto Y; simultaneamente, a thread B já mantém um bloqueio
para o objeto Y e tenta conseguir um bloqueio para o objeto X;
nenhuma das threads podem mais fazer progressos
– Sinais perdidos (missed signals) – uma thread permanece
dormente porque começou a esperar depois que uma
notificação para despertá-la foi produzida
Vivacidade
• Exemplos de falhas liveness potencialmente permanentes
incluem (continuação):
– Bloqueios de monitoramento aninhados - uma thread em
espera mantém um bloqueio que poderia ser necessário a
qualquer outra thread que tente despertá-la
– Livelock - uma ação contínua de nova tentativa (retry),
continuamente falha
– Inanição (starvation) - a JVM/SO falha, nunca alocando tempo
de CPU para uma thread, o que pode acontecer devido a
políticas de agendamento ou mesmo ataques hostis de negação
de serviços, no computador host
Vivacidade
• Exemplos de falhas liveness potencialmente permanentes
incluem (continuação):
– Esgotamento de recursos - um grupo de threads em conjunto
detêm todos de um número finito de recursos, uma delas
necessita de recursos adicionais, mas nenhuma thread irá
desistir em favor de outra
– Falha Distribuída - uma máquina remota conectada por um
socket servindo como um InputStream falha ou se torna
inacessível
Performance
• Forças baseadas no desempenho estendem conceitos de
vivacidade
• Além de exigir que cada método invocado,
eventualmente, execute, metas de desempenho os
obrigam a executar breve e de maneira rápida
• Embora não consideraremos sistemas de tempo real
hard, em que falhas de execução dentro de um
determinado intervalo de tempo podem levar a erros
catastróficos do sistema, quase todos os programas
concorrentes têm metas de desempenho implícitas ou
explícitas
Performance
• Requisitos de desempenho significativos são expressos
em termos de qualidades mensuráveis, incluindo as
métricas apresentadas a seguir
• O objetivo pode ser expresso em uma tendência central
(por exemplo, média, mediana) das medições, assim
como em sua variabilidade (por exemplo, faixa de
valores, desvio padrão)
Medidas de Desempenho
• Rendimento (throughput) - o número de operações realizadas
por unidade de tempo, as operações de interesse podem
variar de métodos individuais até a execução inteira do
programa
– Na maioria das vezes, o rendimento não é relatado como uma taxa,
mas em vez disso, como o tempo levado para executar uma operação
• Latência - o tempo decorrido entre a emissão de uma
mensagem (via por exemplo, um clique do mouse, a
invocação de método, ou conexão de socket de entrada) e de
sua realização
– Em contextos onde as operações são uniformes, single-threaded, e
"continuamente" solicitadas, a latência é apenas o inverso do
rendimento
– Mas, mais tipicamente, as latências de interesse refletem os tempos
de resposta - os atrasos até que algo aconteça, não necessariamente
a conclusão integral de um método ou serviço
Medidas de Desempenho
• Capacidade - o número de atividades simultâneas que
podem ser realizadas por um determinado alvo em um
rendimento mínimo ou latência máxima
– Especialmente em aplicações de rede, isso pode servir como um
indicador útil de disponibilidade geral, uma vez que reflete o
número de clientes que podem ser atendidos sem deixar a
conexões cair devido a time-outs ou sobrecargas da fila da rede
• Eficiência - rendimento dividido pela quantidade de
recursos computacionais (por exemplo, CPUs, memória e
dispositivos IO) necessários para obter esse rendimento
Medidas de Desempenho
• Escalabilidade - a taxa à qual a latência ou o rendimento
melhora quando os recursos (novamente, geralmente
CPUs, memória, ou dispositivos) são adicionados a um
sistema
– Medidas relacionadas incluem utilização – o percentual de
recursos disponíveis que são aplicados a uma tarefa de
interesse
• Degradação - a taxa em que a latência ou o rendimento
piora à medida que mais clientes, atividades ou
operações são adicionados sem a adição de recursos
Performance
• A maioria dos projetos multithreaded aceitam
implicitamente um pequeno trade-off de eficiência
computacional ruim para obter uma melhor latência e
escalabilidade
• Suporte a concorrência introduz os tipos de sobrecarga e
de contenção apresentados a seguir, que podem tornar
programas lentos
Sobrecarga e Contenção
• Bloqueios - um método sincronizado normalmente requer
uma maior sobrecarga de chamada do que um método não
sincronizado, além disso, os métodos que frequentemente
bloqueiam esperam pelos bloqueios (ou por qualquer outro
motivo), sendo mais lentos do que aqueles que não o fazem
• Monitores - Object.wait, Object.notify,
Object.notifyAll, e os métodos deles derivados (como
Thread.join) podem ser mais custosos do que outras
operações de suporte a tempo de execução da JVM básica
• Threads - criar e iniciar uma thread é tipicamente mais caro
do que a criação de um objeto comum e chamar um método
dele
Sobrecarga e Contenção
• Mudança de contexto (context-switching) - o
mapeamento de threads para CPUs encontra uma
sobrecarga de mudança de contexto quando a JVM/OS
salva o estado da CPU associado a uma thread, seleciona
outra thread a ser executada, e carrega o estado da CPU
associado
• Agendamento - cálculos e políticas subjacentes que
selecionam qual thread está elegíveis para executar
adiciona uma sobrecarga, estas podem ainda interagir
com outras tarefas do sistema, tais como processamento
assíncrono de eventos e coleta de lixo
Sobrecarga e Contenção
• Localidade - em multiprocessadores, quando várias
threads estão em execução em diferentes CPUs
compartilhando o acesso aos mesmos objetos, hardware
de consistência de cache e software de sistema de baixo
nível devem comunicar os valores associados entre os
processadores
Sobrecarga e Contenção
• Algoritmos - alguns algoritmos sequenciais eficientes não
se aplicam em ambientes concorrentes
– Por exemplo, algumas estruturas de dados que dependem de
rotinas de cache onde apenas se sabe que exatamente uma
tread executa todas as operações
– No entanto, também existem algoritmos concorrentes
alternativos eficientes para muitos problemas, incluindo
aqueles que criam a possibilidade de novos aumentos de
velocidade através do paralelismo
• As sobrecargas gerais associadas com construtores de
concorrência diminuem de forma constante com a
melhora das JVMs
Reusabilidade
• Uma classe ou objeto é reutilizável na medida em que ela
pode ser facilmente empregada em diferentes contextos,
tanto como um componente de caixa preta ou como a
base de extensão de caixa branca através de subclasses e
técnicas relacionadas
• A interação entre as preocupações de segurança e
vivacidade podem afetar significativamente a capacidade
de reutilização
• Geralmente é possível projetar componentes para serem
seguros em todos os contextos possíveis
– Por exemplo, um método sincronizado que se recusa a iniciar
até que ele possua o bloqueio de sincronização irá fazer isso,
não importa como ele é usado
Reusabilidade
• Mas em alguns desses contextos, programas usando esse
componente seguro pode encontrar falhas de vivacidade
(por exemplo, o deadlock)
• Por outro lado, a funcionalidade em torno de um
componente utilizando apenas métodos não
sincronizados será sempre “vivo” (pelo menos no que diz
respeito ao bloqueio), mas podem encontrar violações de
segurança quando múltiplas execuções concorrentes
ocorrem
Reusabilidade
• As dualidades de segurança e vivacidade são refletidas em
alguns pontos de vista extremos de metodologia de projeto:
– Algumas estratégias de projeto top-down tomam uma abordagem de
segurança pura em primeiro lugar: certifica-se de que cada classe e
objeto é seguro, e, posteriormente, tenta melhorar a vivacidade como
uma medida de otimização
– Em oposto, a abordagem bottom-up é muitas vezes adotada em
programação de sistemas concorrentes: certifica-se de que o código é
executado, e, em seguida, tenta dividi-lo em camadas de recursos de
segurança, por exemplo, adicionando bloqueios
• Nenhum dos extremos é especialmente bem sucedido na
prática: é muito fácil que abordagens top-down resultem em
sistemas lentos, propensos a impasses, e que abordagens
bottom-up resultem em código com erros, com violações de
segurança não previstas
Reusabilidade
• Geralmente é mais produtivo prosseguir com o
entendimento de que alguns componentes muito úteis e
eficientes não são, e não precisam ser, absolutamente
seguros, e que serviços úteis realizados por alguns
componentes não são absolutamente “vivos”
• Em vez disso, eles operam corretamente somente em
determinados contextos de uso restrito
• Portanto, estabelecer, documentar, publicar e explorar
esses contextos tornam-se questões centrais no projeto
de software concorrente
Reusabilidade
• Existem duas abordagens gerais (e uma gama de opções
intermédias) para lidar com a dependência de contexto:
– (1) minimizar a incerteza “fechando” partes de sistemas, e
– (2) estabelecer políticas e protocolos que permitam que os
componentes se tornem ou permaneçam abertos
• Muitos esforços práticos de design envolvem um pouco
de cada
Subsistemas Fechados
Subsistemas Fechados
• Um sistema fechado ideal é aquele para o qual temos de
maneira perfeita e estática (tempo de projeto) o
conhecimento sobre todos os comportamentos possíveis
• Isto é tipicamente tanto inatingível como indesejável, no
entanto, muitas vezes é possível fechar partes de
sistemas, em unidades individuais que variam a partir de
classes de componentes a nível de produto, através do
emprego de versões possivelmente extremas de técnicas
de encapsulamento OO apresentadas a seguir
Técnicas de Encapsulamento OO
• Comunicação externa restrita - todas as interações, tanto
internas como externas, ocorrem através de uma
interface reduzida, no caso mais tratável, o subsistema é
fechado à comunicação, nunca internamente invocando
métodos em objetos fora do subsistema
• Estrutura interna determinística - a natureza concreta de
todos os objetos e threads que compõem o subsistema é
estaticamente conhecida, as palavras-chave final e
private podem ser usadas para ajudar a impor isso
Subsistemas Fechados
• Em pelo menos alguns desses sistemas, podemos, em
princípio, provar - informalmente, formalmente, ou até
mesmo mecanicamente - que não são possíveis violações
de segurança interna ou de vivacidade, dentro de um
componente fechado
• Ou, se elas são possíveis, podemos continuar a refinar
projetos e implementações até que um componente
esteja provavelmente correto
• No melhor dos casos, podemos, então, aplicar esse
conhecimento de composição para analisar outras partes
de um sistema que contam com este componente
Subsistemas Fechados
• Informações estáticas perfeitas sobre objetos, threads e
interações nos dizem não só o que pode acontecer, mas
também o que não pode acontecer
– Por exemplo, pode ser o caso que, embora dois métodos
sincronizados em dois objetos contenham chamadas um para o
outro, eles nunca podem ser acessados simultaneamente por
diferentes threads dentro do subsistema, desse modo o
impasse nunca ocorrerá
Subsistemas Fechados
• Fechamento também pode apresentar novas
oportunidades de otimização manual ou dirigida pelo
compilador
– Por exemplo, a remoção de sincronização de métodos que
normalmente poderiam requere-lo, ou empregar algoritmos
inteligentes de propósito especial que podem ser desenvolvidos
para aplicação apenas pela eliminação de possibilidade de
interação indesejada
– Sistemas embarcados são muitas vezes compostos como
coleções de módulos fechados, em parte para melhorar a
previsibilidade, escalonabilidade e performance
Subsistemas Fechados
• Embora os subsistemas fechados sejam tratáveis, eles
também podem ser frágeis
• Quando as restrições e premissas que governam a sua
estrutura interna mudam, esses componentes são muitas
vezes descartados e reconstruídos a partir do zero
Subsistemas Abertos
• Um sistema aberto ideal é infinitamente extensível,
através de várias dimensões:
– Pode carregar classes desconhecidas de forma dinâmica,
permitir que subclasses substituam qualquer método, empregar
callbacks entre objetos de diferentes subsistemas, compartilhar
recursos comuns entre threads, usar reflexão para descobrir e
invocar métodos em objetos de outra maneira desconhecidos, e
assim por diante
• Abertura ilimitada é geralmente tão inatingível e
indesejável quanto fechamento completo: se tudo pode
mudar, então não podemos programar nada
• Mas a maioria dos sistemas exigem, pelo menos, alguma
dessa flexibilidade
Subsistemas Abertos
• Análise estática completa de sistemas abertos não é possível
ainda, uma vez que a sua natureza e estrutura evoluem ao
longo do tempo
• Em vez disso, os sistemas abertos devem contar com políticas
e protocolos documentados a que cada componente adere
• A Internet é um dos melhores exemplos de um sistema
aberto:
– Ela continuamente evolui, por exemplo, adicionando novos
hospedeiros, páginas web, e serviços, exigindo apenas que todos os
participantes obedeçam a algumas políticas e protocolos de rede
– Tal como acontece com outros sistemas abertos, a aderência às
políticas e protocolos da Internet é, por vezes, difícil de aplicar
– No entanto, JVMs se organizam de forma que os componentes com
não-conformidade não podem catastroficamente danificar a
integridade do sistema
Subsistemas Abertos
• Projeto orientado por políticas pode funcionar bem em
nível muito menor de sistemas concorrentes típicos, onde
as políticas e protocolos muitas vezes tomam a forma de
regras de projeto
• Exemplos de domínios de políticas incluem:
– Fluxo - por exemplo, uma regra na forma: componentes do tipo
A enviam mensagens para os do tipo B, mas nunca vice-versa
– Bloqueio - por exemplo, uma regra na forma: métodos do tipo A
sempre disparam exceções imediatamente se o recurso R não
está disponível, em vez de bloquear até que esteja disponível
– Notificações - por exemplo, uma regra na forma: Objetos do
tipo A sempre enviam notificações de mudança aos seus
ouvintes (listeners), sempre que atualizados
Subsistemas Abertos
• Adopção de um número relativamente pequeno de
políticas simplifica o projeto, minimizando a possibilidade
de decisões inconsistentes caso a caso
• Autores de componentes, talvez com a ajuda de revisores
de código e ferramentas, precisam checar apenas se eles
estão obedecendo as regras de projeto relevantes, e de
outra forma podem focar a atenção nas tarefas em mãos,
desenvolvedores podem pensar localmente, enquanto
ainda agindo globalmente
Subsistemas Abertos
• No entanto, o design orientado a política pode se tornar
incontrolável quando o número de políticas cresce muito e as
obrigações de programação que elas induzem sobrecarregam
desenvolvedores
• Quando os métodos, mesmo simples, como atualização de
um saldo de conta ou impressão "Olá, mundo" exigem
dezenas de linhas de código estranho propenso a erros de
conformidade com políticas de projeto, é hora de tomar
algum tipo de ação de reparação:
– Simplificar ou reduzir o número de políticas
– Criar ferramentas que ajudam a automatizar a geração de código e/ou
a verificação de conformidade
– Criar linguagens específicas de domínio que impõem uma
determinada disciplina; ou
– Criar ambientes e bibliotecas que reduzam a necessidade de que
tanto código de suporte tenha que ser escrito dentro de cada método
Subsistemas Abertos
• Enquanto induzir maior fechamento nos permite otimizar
o desempenho, realizar uma maior abertura permite que
otimizemos para mudanças futuras
• Estes dois tipos de afinações e refatorações são muitas
vezes igualmente desafiadoras, mas têm efeitos opostos:
– Otimizar para desempenho geralmente implica em explorar
casos especiais por decisões de projeto hard-wired (conexão
direta permanente)
– Otimizar para extensibilidade implica na remoção de decisões
hard-wired permitindo a variação, por exemplo, através de
encapsulamento através de métodos substituíveis, ou abstração
da funcionalidade através de interfaces que podem ser re-
implementadas de formas completamente diferentes por
componentes carregados dinamicamente
Subsistemas Abertos
• Porque programas concorrentes tendem a incluir mais
decisões políticas do que os programas sequenciais, e
tendem a depender mais pesadamente de invariantes
envolvendo escolhas particulares de representação,
classes que envolvem construtores de concorrência
muitas vezes acabam por exigir uma atenção especial, a
fim de serem facilmente extensíveis
• Este fenômeno é muito comum, o suficiente para ter um
nome especial, chamado anomalia de herança
Subsistemas Abertos
• No entanto, algumas outras técnicas de programação
desnecessariamente restringem extensibilidade por
razões de desempenho
• Essas táticas se tornam mais questionáveis em melhora
de compiladores e JVMs
• Por exemplo, a compilação dinâmica permite que muitos
componentes extensíveis sejam tratados como se eles
fossem fechados no momento de carregamento da
classe, levando a otimizações e especializações que
exploram determinados contextos de tempo de execução
de forma mais eficaz do que qualquer programador
poderia
Documentação
• Quando a composicionalidade é dependente do
contexto, é vital para o uso pretendido, que contextos e
restrições envolvendo componentes sejam bem
compreendidos e bem documentados
• Quando esta informação não é fornecida, utilização,
reutilização, manutenção, testes, gerenciamento de
configuração, a evolução do sistema e as preocupações
de engenharia de software relacionadas se tornam muito
mais difíceis
Documentação
• A documentação pode ser usada para melhorar a
compreensibilidade por qualquer um dos vários públicos:
outros desenvolvedores usando uma classe como um
componente de caixa-preta, autores de subclasses, os
desenvolvedores que mais tarde irão manter, modificar
ou reparar o código, testadores e revisores de código, e
usuários do sistema
• Através destas audiências, o primeiro objetivo é o de
eliminar a necessidade de uma documentação extensa,
minimizando o inesperado, e, portanto, reduzindo a
complexidade conceitual via: padronização, clareza e
código auxiliar
Padronização
• Usando políticas, protocolos e interfaces comuns
• Por exemplo:
– A adoção de padrões de projeto, referenciando livros, páginas
da web ou documentos de projeto que os descrevem de
maneira mais plena
– Empregando bibliotecas utilitárias padrão e frameworks
– Usando idiomas de codificação padrão e convenções de nomes
– Realizando buscas através de listas de revisão padrão que
enumeram erros comuns
Clareza
• Utilizando as expressões de código mais simples e em sua
maioria autoexplicativas
• Por exemplo:
– Utilizar exceções para advertir sobre condições verificadas
– Expressando restrições internas via qualificadores de acesso
(como private)
– Adoção de padrões comuns de nomenclatura e assinatura, por
exemplo, determinação de que, salvo indicação em contrário, os
métodos que podem bloquear declaram que eles lançam uma
InterruptedException
Código Auxiliar
• Fornecimento de código que demonstra usos pretendidos
• Por exemplo:
– Incluindo amostras ou exemplos de uso recomendadas
– Fornecimento de trechos de código que alcançam efeitos não-
óbvios
– Incluindo métodos projetados a servir como auto testes
Documentação
• Após eliminar a necessidade de explicações óbvias via
documentação, formas mais úteis de documentação
podem ser usadas para esclarecer decisões de projeto
• Os detalhes mais críticos podem ser expressos de forma
sistemática, usando anotações semiformais mostradas na
tabela a seguir
Anotações Semiformais
PRE Pré-condição (não necessariamente checada)
/** PRE: Caller holds synch lock …
WHEN Condição de guarda (sempre checada)
/** WHEN: not empty return oldest …
POST Pós-condição (geralmente não checada)
/** POST: Resource r is released …
OUT Garantia de envio de mensagem (por exemplo callback)
/** OUT: c.process(buff) called after read …
RELY Propriedade requerida (geralmente não checada) de outros objetos e
métodos
/** RELY: Must be awakened by x.signal() …
INV Uma restrição de objeto verdadeira no início e no final de cada
método público
/** INV: x,y are valid screen coordinates …
INIT Uma restrição de objeto que deve ser mantida na construção
/** INIT: bufferCapacity greater than zero …
Documentação
• Documentação adicional, menos estruturada pode ser usada
para explicar as restrições não-óbvias, limitações contextuais,
suposições e decisões de design que impactem o uso em um
ambiente concorrente
• É impossível fornecer uma lista completa de construções que
necessitam desse tipo de documentação, mas casos típicos
incluem:
– Informações de design de alto nível sobre o estado e as restrições de
métodos
– Limitações de segurança conhecidas devido à falta de bloqueio em
situações que exigem isso
– O fato de um método poder bloquear indefinidamente à espera de
uma condição, evento ou recurso
– Métodos destinados a serem chamados apenas a partir de outros
métodos, talvez em outras classes
Padrões de Projeto
• Muitos projetos concorrentes são melhor descritos como
padrões
• Um padrão encapsula uma forma de projeto comum e bem
sucedida, geralmente uma estrutura de objeto (também
conhecida como um microarquitetura) que consiste em uma
ou mais interfaces, classes e/ou objetos que obedecem a
certas restrições e relacionamentos estáticos e dinâmicos
• Padrões são um veículo ideal para a caracterização de
desenhos e técnicas que não precisam ser implementadas
exatamente da mesma maneira em diferentes contextos, e,
portanto, não podem ser encapsuladas de maneira útil como
componentes reutilizáveis
Padrões de Projeto
• Os componentes reutilizáveis e frameworks podem
desempenhar um papel central no desenvolvimento de
software concorrente, mas grande parte da programação
OO concorrente implica em reutilização, adaptação e
extensão das recorrentes formas e práticas de projeto e
não de classes particulares
Aplicação de Camadas
• Estratificação de controle de políticas sobre mecanismos é um
princípio estruturante comum em sistemas de todos os tipos
• Muitas técnicas de estratificação e de composição OO contam
com “imprensar” alguma chamada de método ou corpo de
código entre ações antes e depois
• Todas as formas de controle antes/depois providenciam que
um determinado método seja interceptado por isso, sempre
executam a sequência:
before(); method(); after();
• Ou, para assegurar que ações depois sejam realizadas mesmo
se os métodos centrais encontrarem exceções:
before();
try { method(); }
finally { after(); }
Aplicação de Camadas
• Por exemplo, um método sincronized adquire um
bloqueio antes de executar o código dentro do método, e
libera o bloqueio após o método ser concluído
• Mas as ideias básicas de padrões antes/depois podem ser
ilustradas em conjunto com outra prática útil na
programação OO, código de autocontrole:
– Os campos de qualquer objeto devem preservar todas as
invariantes sempre que o objeto não está envolvido em um
método público
• Invariantes devem ser mantidas mesmo que esses
métodos lancem qualquer uma das suas exceções
declaradas, a menos que essas exceções denotem
corrupção ou falha do programa (como pode ocorrer
em RuntimeExceptions e Errors )
Aplicação de Camadas
• Conformidade com as invariantes computáveis ​​pode ser
testada de forma dinâmica através da criação de classes que
as verificam tanto sobre a entrada como na saída de cada
método público
• Técnicas semelhantes aplicam-se a pré-condições e pós-
condições, mas para simplificar, vamos ilustrar apenas com
invariantes
• Como exemplo, suponha que nós gostaríamos de criar classes
de tanque de água que contêm um autocontrole sobre a
invariante que o volume esteja sempre entre zero e a
capacidade
• Para fazer isso, podemos definir um método
checkVolumeInvariant e usá-lo tanto antes como
depois da operação
Aplicação de Camadas
• Podemos definir primeiro uma exceção para lançar se a
invariante falhar:
class AssertionError extends java.lang.Error {
public AssertionError() { super(); }
public AssertionError(String message) {
super(message); }
}
• Pode ser perturbador inserir essas verificações
manualmente dentro de cada método
• Em vez disso, um dos três padrões de projeto
antes/depois pode ser usado para separar os controles
dos métodos base: classes de adaptadores, projetos
baseados em subclasses, e classes adaptadoras de
métodos
Aplicação de Camadas
• Em todos os casos, a melhor maneira de configuração é a
definição de uma interface que descreva a funcionalidade
básica
• Interfaces são quase sempre necessárias quando
precisamos dar espaço suficiente para variação nas
implementações
• Por outro lado, a falta de interfaces existentes limita as
opções quando retroativamente aplicamos padrões
antes/depois
Aplicação de Camadas
• Aqui está uma interface descrevendo uma variante
menor da classe tanque de água, técnicas antes/depois
podem ser aplicadas para verificar invariantes em torno
da operação transferWater:
interface Tank {
float getCapacity();
float getVolume();
void transferWater(float amount)
throws OverflowException,
UnderflowException;
}
Adaptadores
• Quando interfaces padronizadas são definidas depois do
projeto de uma ou mais classes concretas, essas classes,
muitas vezes não implementam a interface desejada, por
exemplo, os nomes de seus métodos podem ser
ligeiramente diferentes dos definidos na interface
• Se não podemos modificar essas classes concretas para
corrigir esses problemas, podemos obter o efeito
desejado através da construção de uma classe Adapter
que traduz as incompatibilidades
Adaptadores
• Digamos que tenhamos uma classe Performer que
possui o método perform e cumpre todas as
qualificações para ser usada como um Runnable exceto
pelo desencontro de nomes
• É possível construir um Adapter para que ela possa ser
usada em uma thread por alguma outra classe:
Adaptadores
class AdaptedPerformer implements Runnable {
private final Performer adaptee;
public AdaptedPerformer(Performer p) {
adaptee = p; }
public void run() { adaptee.perform(); }
}
• Este é apenas um dos muitos contextos comuns para
construção de adaptadores, que também formam a base
de vários padrões relacionados apresentados no livro
Design Patterns
– Um Proxy é um adaptador com a mesma interface que o seu
delegado
– Um Composite mantém uma coleção de delegados, todos
dando suporte à mesma interface
Adaptadores
• Neste estilo baseado em delegação de composição, a
classe host de acesso público delega todos os métodos
para um ou mais delegados e recebe de volta respostas,
talvez fazendo alguma tradução (mudanças de nome,
conversão de parâmetros, filtragem de resultados, etc.)
que cerca as chamadas aos delegados
• Os adaptadores podem ser usados ​​para fornecer controle
antes/depois meramente envolvendo a chamada
delegada dentro das ações de controle
Adaptadores
• Por exemplo, supondo que temos uma classe de
implementação, chamada TankImpl , podemos escrever
a seguinte classe AdaptedTank, que pode ser usada em
vez da original em alguma aplicação, substituindo todas
as ocorrências de:
new TankImpl(...)
• Por:
new AdaptedTank(new TankImpl(...))
AdaptedTank
class AdaptedTank implements Tank {
protected final Tank delegate;
public AdaptedTank(Tank t) { delegate = t; }
public float getCapacity() {
return delegate.getCapacity(); }
public float getVolume() {
return delegate.getVolume(); }
protected void checkVolumeInvariant() throws
AssertionError {
float v = getVolume();
float c = getCapacity();
if ( !(v >= 0.0 && v <= c) )
throw new AssertionError();
}
AdaptedTank
public synchronized void transferWater(
float amount) throws OverflowException,
UnderflowException {
checkVolumeInvariant(); // before-check
try {
delegate.transferWater(amount);
}
// The re-throws will be postponed until
// after-check in the finally clause
catch (OverflowException ex) { throw ex; }
catch (UnderflowException ex) { throw ex; }
finally {
checkVolumeInvariant(); // after-check
}
}
}
Subclasses
• No caso normal, quando as versões dos métodos
interceptados antes/depois têm os mesmos nomes e
usos das versões de base, subclasse pode ser uma
alternativa mais simples do que a utilização de
adaptadores
• Versões subclasse de métodos podem interpor
verificações em torno de chamadas para suas versões
super
Exemplo de Subclasses
class SubclassedTank extends TankImpl {
protected void checkVolumeInvariant() throws
AssertionError {
// ... identical to AdaptedTank version ...
}
public synchronized void transferWater(
float amount) throws OverflowException,
UnderflowException {
// identical to AdaptedTank version except
// for inner call:
// ...
try {
super.transferWater(amount);
}
// ...
}
}
Exemplo de Subclasses
Subclasses X Adaptadores
• Algumas escolhas entre subclasses e adaptadores são apenas
uma questão de estilo, outras refletem diferenças entre
delegação e herança
• Adaptadores permitem manipulações que escapam às regras
de subclassificação, por exemplo, não podemos substituir
um método público como privado em uma subclasse, a fim de
desabilitar o acesso, mas podemos simplesmente deixar de
delegar o método em um adaptador
• Várias formas de delegação podem até ser usadas como um
substituto de tipos para subclassificação fazendo com que
cada classe "sub" (Adapter) mantenha uma referência a uma
instância de sua classe "super" (Adaptee), delegando todas as
operações "herdadas"
Subclasses X Adaptadores
• Delegação também pode ser mais flexível do que
subclassificação, uma vez que objetos "sub" podem até
mesmo mudar seus "supers" dinamicamente (através da
redefinição da referência delegada)
• Delegação pode também ser usada para obter os efeitos
de herança múltipla, por exemplo, se uma classe deve
implementar duas interfaces independentes,
chamadas Tank e java.awt.event.ActionListener, e
há duas superclasses disponíveis que fornecem a
funcionalidade necessária, então uma destas pode ser
uma subclasse e o outro delegado
Subclasses X Adaptadores
• No entanto, a delegação é menos poderosa do que
subclassificação em alguns outros aspectos
• Por exemplo, auto-chamadas em "superclasses" não são
automaticamente vinculadas às versões dos métodos que
foram “sobrescritos” em “subclasses” baseadas em
delegação
• Projetos de adaptadores também podem cair em
problemas que giram em torno do fato de que os objetos
Adaptee e Adapter são objetos diferentes, por exemplo,
testes de igualdade de referência a objetos devem ser
realizados com mais cuidado uma vez que um teste para
ver se temos a versão Adaptee de um objeto falha se
temos a versão Adapter, e vice-versa
Métodos de Modelo
• Quando estamos bastante certos de que iremos utilizar
um controle antes/depois em um conjunto de classes
relacionadas, podemos criar uma classe abstrata que
automatiza a sequência de controle através de uma
aplicação do padrão Template Method (que não tem
nada a ver com tipos genéricos C++)
Métodos de Modelo
Métodos de Modelo
• Uma classe abstrata que dá suporte a métodos de
modelo estabelece um ambiente que facilita a construção
de subclasses que podem substituir as ações base,
métodos antes/depois, ou ambos:
– Código de ação no nível básico é definido em métodos não-
públicos (por convenção, chamamos a versão não-pública de
qualquer método method como doMethod), um pouco menos
flexíveis, estes métodos não precisam ser declaradas não-
públicos se forem concebidos para ser sobrescritos em
subclasses
– Operações antes e depois também são definidas como métodos
não-públicos
– Métodos públicos invocam os métodos base entre os métodos
antes e depois
Métodos de Modelo
• Aplicando ao exemplo Tank:
abstract class AbstractTank implements Tank {
protected void checkVolumeInvariant() throws
AssertionError {
// ... identical to AdaptedTank version ...
}
protected abstract void doTransferWater(float amount)
throws OverflowException, UnderflowException;
public synchronized void transferWater(float amount)
throws OverflowException, UnderflowException {
// identical to AdaptedTank version except
// for inner call:
// ...
try {
doTransferWater(amount);
}
// ...
}
}
Métodos de Modelo
• Aplicando ao exemplo Tank (continuação):
class ConcreteTank extends AbstractTank {
protected final float capacity;
protected float volume;
// ...
public float getVolume() { return volume; }
public float getCapacity() { return capacity;}
protected void doTransferWater(float amount)
throws OverflowException,
UnderflowException {
// ... implementation code ...
}
}
Adaptadores de Método
• A abordagem mais flexível, mas, por vezes, mais difícil de
controle antes/depois é definir uma classe cujo propósito
é o de chamar um método específico em um
determinado objeto
• No padrão Command Object e em suas várias versões, as
instâncias dessas classes podem então ser passadas,
manipuladas, e, finalmente, executadas (aqui, entre as
operações antes/depois)
Adaptadores de Método
• Por causa das regras de tipagem estática, deve haver um tipo
diferente de classe do adaptador para cada tipo de método
que está sendo adaptado
• Para evitar a proliferação de todos esses tipos, a maioria das
aplicações restringem a atenção para apenas um ou um
pequeno conjunto de interfaces genéricas, cada uma
definindo um único método
• Por exemplo, a classe Thread e a maioria dos outros
frameworks de execução aceitam apenas instâncias da
interface Runnable para invocar seus métodos run,
contendo argumentos, resultado e exceções
• Da mesma forma, definimos e usamos a interface
Callable contendo apenas um método call que aceita
um argumento Object, retorna um Object , e pode lançar
qualquer Exception
Adaptadores de Método
• Podemos aplicar uma versão de antes/depois de divisão
em camadas com base nos adaptadores de método
primeiro pela definição de uma interface TankOp:
interface TankOp {
void op() throws OverflowException,
UnderflowException;
}
• No código de exemplo apresentado a seguir,
estranhamente, todas as utilizações de adaptadores de
método são locais para uma classe
TankWithMethodAdapter
• Além disso, neste pequeno exemplo, há apenas um
método adaptável
Adaptadores de Método
• No entanto, o mesmo processo poderia ser utilizado para
quaisquer outros métodos de Tank definidos nesta classe
ou em suas subclasses
• Adaptadores de método são muito mais comuns em
aplicações onde instâncias devem ser registradas e/ou
transmitidas entre vários objetos antes de serem
executadas, o que justifica os custos extras de instalação
e obrigações de programação
Adaptadores de Método
class TankWithMethodAdapter {
// ...
protected void checkVolumeInvariant() throws
AssertionError {
// ... identical to AdaptedTank version ...
}
protected void runWithinBeforeAfterChecks
(TankOp cmd) throws OverflowException,
UnderflowException {
// identical to AdaptedTank.transferWater
// except for inner call:
// ...
try {
cmd.op();
}
// ...
}
Adaptadores de Método
protected void doTransferWater(float amount)
throws OverflowException,
UnderflowException {
// ... implementation code ...
}
public synchronized void transferWater(final float
amount) throws OverflowException,
UnderflowException {
runWithinBeforeAfterChecks(new TankOp() {
public void op() throws OverflowException,
UnderflowException {
doTransferWater(amount);
}
});
}
}
Adaptadores de Método
• Algumas aplicações de adaptadores de método podem
ser parcialmente automatizadas através de recursos de
reflexão
• Um construtor genérico pode sondar uma classe para um
determinado java.lang.reflect.Method, configurar
argumentos, invoca-lo, e transferir de volta resultados
• Isto tem o preço de maior sobrecarga, bem como a
necessidade de lidar com as muitas exceções que podem
surgir
• Portanto, geralmente só vale a pena quando se lida com
código desconhecido carregado dinamicamente
UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA
CURSO DE CIÊNCIA DA COMPUTAÇÃO
PROGRAMAÇÃO CONCORRENTE – 2015.1
Fábio M. Pereira
(fabio.mpereira@uesb.edu.br)

Mais conteúdo relacionado

Mais procurados

Super computers by rachna
Super computers by  rachnaSuper computers by  rachna
Super computers by rachna
Rachna Singh
 
Distributed Systems
Distributed SystemsDistributed Systems
Distributed Systems
Rupsee
 

Mais procurados (20)

Sistemas Operacionais - Aula 06 (Estrutura do S.O)
Sistemas Operacionais - Aula 06 (Estrutura do S.O)Sistemas Operacionais - Aula 06 (Estrutura do S.O)
Sistemas Operacionais - Aula 06 (Estrutura do S.O)
 
história da computação
história da computaçãohistória da computação
história da computação
 
Sistemas Operacionais - Aula 08 (Sincronização e Comunicação entre Processos)
Sistemas Operacionais - Aula 08 (Sincronização e Comunicação entre Processos)Sistemas Operacionais - Aula 08 (Sincronização e Comunicação entre Processos)
Sistemas Operacionais - Aula 08 (Sincronização e Comunicação entre Processos)
 
Aula04 Sistemas Distribuídos - Processos
Aula04 Sistemas Distribuídos - ProcessosAula04 Sistemas Distribuídos - Processos
Aula04 Sistemas Distribuídos - Processos
 
Super computers by rachna
Super computers by  rachnaSuper computers by  rachna
Super computers by rachna
 
3rd generation computer
3rd generation computer3rd generation computer
3rd generation computer
 
Grid computing
Grid computingGrid computing
Grid computing
 
Distributed Systems
Distributed SystemsDistributed Systems
Distributed Systems
 
Aula 06-sistemas de-arquivo
Aula 06-sistemas de-arquivoAula 06-sistemas de-arquivo
Aula 06-sistemas de-arquivo
 
Interrupciones
InterrupcionesInterrupciones
Interrupciones
 
Módulo 3-Sistema Operativo Servidor - V3.pdf
Módulo 3-Sistema Operativo Servidor - V3.pdfMódulo 3-Sistema Operativo Servidor - V3.pdf
Módulo 3-Sistema Operativo Servidor - V3.pdf
 
Aula 11 - História dos Sistemas Operacionais - Windows
Aula 11 - História dos Sistemas Operacionais - WindowsAula 11 - História dos Sistemas Operacionais - Windows
Aula 11 - História dos Sistemas Operacionais - Windows
 
Formatação
FormataçãoFormatação
Formatação
 
Sistemas Operacionais Modernos Capítulo 3 Deadlock
Sistemas Operacionais Modernos Capítulo 3 DeadlockSistemas Operacionais Modernos Capítulo 3 Deadlock
Sistemas Operacionais Modernos Capítulo 3 Deadlock
 
Distributed system architecture
Distributed system architectureDistributed system architecture
Distributed system architecture
 
Grid computing
Grid computingGrid computing
Grid computing
 
CISC e RISC
CISC e RISCCISC e RISC
CISC e RISC
 
Aula 8 (backup)
Aula 8 (backup)Aula 8 (backup)
Aula 8 (backup)
 
Interrupt handling
Interrupt handlingInterrupt handling
Interrupt handling
 
CS6601 DISTRIBUTED SYSTEMS
CS6601 DISTRIBUTED SYSTEMSCS6601 DISTRIBUTED SYSTEMS
CS6601 DISTRIBUTED SYSTEMS
 

Destaque

Paradigmas de Linguagens de Programação - Escopo estático/dinâmico
Paradigmas de Linguagens de Programação - Escopo estático/dinâmicoParadigmas de Linguagens de Programação - Escopo estático/dinâmico
Paradigmas de Linguagens de Programação - Escopo estático/dinâmico
Adriano Teixeira de Souza
 
Comparativo entre Go e Lua
Comparativo entre Go e LuaComparativo entre Go e Lua
Comparativo entre Go e Lua
gpolo
 
Modelo orientado a objetos
Modelo orientado a objetosModelo orientado a objetos
Modelo orientado a objetos
Daiana de Ávila
 

Destaque (20)

Programação Concorrente - Introdução
Programação Concorrente - IntroduçãoProgramação Concorrente - Introdução
Programação Concorrente - Introdução
 
Programação Concorrente - Gerenciamento de Threads - Parte I
Programação Concorrente - Gerenciamento de Threads - Parte IProgramação Concorrente - Gerenciamento de Threads - Parte I
Programação Concorrente - Gerenciamento de Threads - Parte I
 
Concorrência na Linguagem de Programação
Concorrência na Linguagem de ProgramaçãoConcorrência na Linguagem de Programação
Concorrência na Linguagem de Programação
 
Inglês doenças
Inglês doençasInglês doenças
Inglês doenças
 
05 - Sincronização de Threads - I
05 - Sincronização de Threads - I05 - Sincronização de Threads - I
05 - Sincronização de Threads - I
 
Programação Concorrente - Gerenciamento de Threads - Parte II
Programação Concorrente - Gerenciamento de Threads - Parte IIProgramação Concorrente - Gerenciamento de Threads - Parte II
Programação Concorrente - Gerenciamento de Threads - Parte II
 
Aula Tratamento de Exceções
Aula Tratamento de ExceçõesAula Tratamento de Exceções
Aula Tratamento de Exceções
 
Paradigmas de Linguagens de Programação - Escopo estático/dinâmico
Paradigmas de Linguagens de Programação - Escopo estático/dinâmicoParadigmas de Linguagens de Programação - Escopo estático/dinâmico
Paradigmas de Linguagens de Programação - Escopo estático/dinâmico
 
Além do java
Além do javaAlém do java
Além do java
 
Comparativo entre Go e Lua
Comparativo entre Go e LuaComparativo entre Go e Lua
Comparativo entre Go e Lua
 
Programação concorrente
Programação concorrenteProgramação concorrente
Programação concorrente
 
Programação Concorrente - Aula 02
Programação Concorrente - Aula 02Programação Concorrente - Aula 02
Programação Concorrente - Aula 02
 
Programação Concorrente - Aula 01
Programação Concorrente - Aula 01Programação Concorrente - Aula 01
Programação Concorrente - Aula 01
 
Paradigma lógico
Paradigma lógicoParadigma lógico
Paradigma lógico
 
Modelo orientado a objetos
Modelo orientado a objetosModelo orientado a objetos
Modelo orientado a objetos
 
Uma visão de mercado das linguagens de programação
Uma visão de mercado das linguagens de programaçãoUma visão de mercado das linguagens de programação
Uma visão de mercado das linguagens de programação
 
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
 
Programação Concorrente - Curso Completo
Programação Concorrente - Curso CompletoProgramação Concorrente - Curso Completo
Programação Concorrente - Curso Completo
 
Prolog 04 - Regras
Prolog 04 - RegrasProlog 04 - Regras
Prolog 04 - Regras
 
Aula de Prolog 06 - Recursão
Aula de Prolog 06 - RecursãoAula de Prolog 06 - Recursão
Aula de Prolog 06 - Recursão
 

Semelhante a Programação Concorrente - Objetos e Concorrência

2010 02 26 Sistemas Operacionais Aula1
2010 02 26 Sistemas Operacionais Aula12010 02 26 Sistemas Operacionais Aula1
2010 02 26 Sistemas Operacionais Aula1
Universal.org.mx
 
II Material de Apoio Sistemas Operacionais
II Material de Apoio Sistemas OperacionaisII Material de Apoio Sistemas Operacionais
II Material de Apoio Sistemas Operacionais
rodfernandes
 
Questoesde fso
Questoesde fsoQuestoesde fso
Questoesde fso
paulocsm
 
Sistemas operacionais sistemas-distribuidos
Sistemas operacionais sistemas-distribuidosSistemas operacionais sistemas-distribuidos
Sistemas operacionais sistemas-distribuidos
robsons75
 
Introducao a Sistemas Operacionais
Introducao a Sistemas OperacionaisIntroducao a Sistemas Operacionais
Introducao a Sistemas Operacionais
Isaac Vieira
 
Sistemas operacionais
Sistemas operacionaisSistemas operacionais
Sistemas operacionais
DuFelix02
 

Semelhante a Programação Concorrente - Objetos e Concorrência (20)

SI - Processos, Threads, Virtualização e Migração de Código
SI - Processos, Threads, Virtualização e Migração de CódigoSI - Processos, Threads, Virtualização e Migração de Código
SI - Processos, Threads, Virtualização e Migração de Código
 
2010 02 26 Sistemas Operacionais Aula1
2010 02 26 Sistemas Operacionais Aula12010 02 26 Sistemas Operacionais Aula1
2010 02 26 Sistemas Operacionais Aula1
 
II Material de Apoio Sistemas Operacionais
II Material de Apoio Sistemas OperacionaisII Material de Apoio Sistemas Operacionais
II Material de Apoio Sistemas Operacionais
 
Questoesde fso
Questoesde fsoQuestoesde fso
Questoesde fso
 
Sistemas operacionais sistemas-distribuidos
Sistemas operacionais sistemas-distribuidosSistemas operacionais sistemas-distribuidos
Sistemas operacionais sistemas-distribuidos
 
Fundamentos da arquitetura cliente servidor.
Fundamentos da arquitetura cliente servidor.Fundamentos da arquitetura cliente servidor.
Fundamentos da arquitetura cliente servidor.
 
Introducao a Sistemas Operacionais
Introducao a Sistemas OperacionaisIntroducao a Sistemas Operacionais
Introducao a Sistemas Operacionais
 
Apresentação HyperCloud GT8
Apresentação HyperCloud GT8Apresentação HyperCloud GT8
Apresentação HyperCloud GT8
 
Joaopinheiro
JoaopinheiroJoaopinheiro
Joaopinheiro
 
Sistemas operacionais
Sistemas operacionaisSistemas operacionais
Sistemas operacionais
 
Sistemas Operacionais
Sistemas OperacionaisSistemas Operacionais
Sistemas Operacionais
 
Aula CARACTERIZAÇÁO DE SISTEMAS distribuidos.pptx
Aula CARACTERIZAÇÁO DE SISTEMAS distribuidos.pptxAula CARACTERIZAÇÁO DE SISTEMAS distribuidos.pptx
Aula CARACTERIZAÇÁO DE SISTEMAS distribuidos.pptx
 
Sistema
SistemaSistema
Sistema
 
Sistemas operativos distribuidos
Sistemas operativos distribuidosSistemas operativos distribuidos
Sistemas operativos distribuidos
 
Sistemas operacionais
Sistemas operacionaisSistemas operacionais
Sistemas operacionais
 
Aula sd 2008_02aspectosprojectosds
Aula sd 2008_02aspectosprojectosdsAula sd 2008_02aspectosprojectosds
Aula sd 2008_02aspectosprojectosds
 
Curso openmp
Curso openmpCurso openmp
Curso openmp
 
Sistemas Operativos
Sistemas OperativosSistemas Operativos
Sistemas Operativos
 
Introducao.2s
Introducao.2sIntroducao.2s
Introducao.2s
 
SI - Introdução a Sistemas Distribuidos
SI - Introdução a Sistemas DistribuidosSI - Introdução a Sistemas Distribuidos
SI - Introdução a Sistemas Distribuidos
 

Mais de Fabio Moura Pereira

Mais de Fabio Moura Pereira (20)

Haskell - Introdução
Haskell - IntroduçãoHaskell - Introdução
Haskell - Introdução
 
04 - Gerenciamento de Threads - II
04 -  Gerenciamento de Threads - II04 -  Gerenciamento de Threads - II
04 - Gerenciamento de Threads - II
 
Aula de Prolog 08 - Unificação
Aula de Prolog 08 - UnificaçãoAula de Prolog 08 - Unificação
Aula de Prolog 08 - Unificação
 
Aula de Prolog 07 - Estruturas de Dados
Aula de Prolog 07 - Estruturas de DadosAula de Prolog 07 - Estruturas de Dados
Aula de Prolog 07 - Estruturas de Dados
 
Aula Prolog 09 - Listas
Aula Prolog 09 - ListasAula Prolog 09 - Listas
Aula Prolog 09 - Listas
 
Aula Prolog - 05
Aula Prolog - 05Aula Prolog - 05
Aula Prolog - 05
 
Aula Prolog 03
Aula Prolog 03Aula Prolog 03
Aula Prolog 03
 
Aula Prolog 02
Aula Prolog 02Aula Prolog 02
Aula Prolog 02
 
Aula Prolog 01
Aula Prolog 01Aula Prolog 01
Aula Prolog 01
 
Aula Persistência 01 (Java)
Aula Persistência 01 (Java)Aula Persistência 01 (Java)
Aula Persistência 01 (Java)
 
Aula Interface Gráfica do Usuário
Aula Interface Gráfica do UsuárioAula Interface Gráfica do Usuário
Aula Interface Gráfica do Usuário
 
Aula Java Swing
Aula Java SwingAula Java Swing
Aula Java Swing
 
Aula - Interfaces e Estilos de Interação
Aula - Interfaces e Estilos de InteraçãoAula - Interfaces e Estilos de Interação
Aula - Interfaces e Estilos de Interação
 
Aula Desenvolvimento de Jogos - Game Engine (Motor de Jogos)
Aula Desenvolvimento de Jogos - Game Engine (Motor de Jogos)Aula Desenvolvimento de Jogos - Game Engine (Motor de Jogos)
Aula Desenvolvimento de Jogos - Game Engine (Motor de Jogos)
 
Padrões de Projeto de Software
Padrões de Projeto de SoftwarePadrões de Projeto de Software
Padrões de Projeto de Software
 
Curso de PHP - Objetos
Curso de PHP - ObjetosCurso de PHP - Objetos
Curso de PHP - Objetos
 
Curso de PHP - Arrays
Curso de PHP - ArraysCurso de PHP - Arrays
Curso de PHP - Arrays
 
Desenvolvimento de Jogos - Game Design
Desenvolvimento de Jogos - Game DesignDesenvolvimento de Jogos - Game Design
Desenvolvimento de Jogos - Game Design
 
Desenvolvimento de Jogos - Mercado Parte 2
Desenvolvimento de Jogos - Mercado Parte 2Desenvolvimento de Jogos - Mercado Parte 2
Desenvolvimento de Jogos - Mercado Parte 2
 
Desenvolvimento de Jogos - Mercado Parte 1
Desenvolvimento de Jogos - Mercado Parte 1Desenvolvimento de Jogos - Mercado Parte 1
Desenvolvimento de Jogos - Mercado Parte 1
 

Último

Responde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptx
Responde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptxResponde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptx
Responde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptx
AntonioVieira539017
 
8 Aula de predicado verbal e nominal - Predicativo do sujeito
8 Aula de predicado verbal e nominal - Predicativo do sujeito8 Aula de predicado verbal e nominal - Predicativo do sujeito
8 Aula de predicado verbal e nominal - Predicativo do sujeito
tatianehilda
 
Teoria heterotrófica e autotrófica dos primeiros seres vivos..pptx
Teoria heterotrófica e autotrófica dos primeiros seres vivos..pptxTeoria heterotrófica e autotrófica dos primeiros seres vivos..pptx
Teoria heterotrófica e autotrófica dos primeiros seres vivos..pptx
TailsonSantos1
 
A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...
A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...
A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...
PatriciaCaetano18
 
Slide - SAEB. língua portuguesa e matemática
Slide - SAEB. língua portuguesa e matemáticaSlide - SAEB. língua portuguesa e matemática
Slide - SAEB. língua portuguesa e matemática
sh5kpmr7w7
 

Último (20)

EDUCAÇÃO ESPECIAL NA PERSPECTIVA INCLUSIVA
EDUCAÇÃO ESPECIAL NA PERSPECTIVA INCLUSIVAEDUCAÇÃO ESPECIAL NA PERSPECTIVA INCLUSIVA
EDUCAÇÃO ESPECIAL NA PERSPECTIVA INCLUSIVA
 
M0 Atendimento – Definição, Importância .pptx
M0 Atendimento – Definição, Importância .pptxM0 Atendimento – Definição, Importância .pptx
M0 Atendimento – Definição, Importância .pptx
 
E a chuva ... (Livro pedagógico para ser usado na educação infantil e trabal...
E a chuva ...  (Livro pedagógico para ser usado na educação infantil e trabal...E a chuva ...  (Livro pedagógico para ser usado na educação infantil e trabal...
E a chuva ... (Livro pedagógico para ser usado na educação infantil e trabal...
 
Estudar, para quê? Ciência, para quê? Parte 1 e Parte 2
Estudar, para quê?  Ciência, para quê? Parte 1 e Parte 2Estudar, para quê?  Ciência, para quê? Parte 1 e Parte 2
Estudar, para quê? Ciência, para quê? Parte 1 e Parte 2
 
Apresentação ISBET Jovem Aprendiz e Estágio 2023.pdf
Apresentação ISBET Jovem Aprendiz e Estágio 2023.pdfApresentação ISBET Jovem Aprendiz e Estágio 2023.pdf
Apresentação ISBET Jovem Aprendiz e Estágio 2023.pdf
 
PROJETO DE EXTENSÃO I - SERVIÇOS JURÍDICOS, CARTORÁRIOS E NOTARIAIS.pdf
PROJETO DE EXTENSÃO I - SERVIÇOS JURÍDICOS, CARTORÁRIOS E NOTARIAIS.pdfPROJETO DE EXTENSÃO I - SERVIÇOS JURÍDICOS, CARTORÁRIOS E NOTARIAIS.pdf
PROJETO DE EXTENSÃO I - SERVIÇOS JURÍDICOS, CARTORÁRIOS E NOTARIAIS.pdf
 
Monoteísmo, Politeísmo, Panteísmo 7 ANO2.pptx
Monoteísmo, Politeísmo, Panteísmo 7 ANO2.pptxMonoteísmo, Politeísmo, Panteísmo 7 ANO2.pptx
Monoteísmo, Politeísmo, Panteísmo 7 ANO2.pptx
 
Responde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptx
Responde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptxResponde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptx
Responde ou passa na HISTÓRIA - REVOLUÇÃO INDUSTRIAL - 8º ANO.pptx
 
8 Aula de predicado verbal e nominal - Predicativo do sujeito
8 Aula de predicado verbal e nominal - Predicativo do sujeito8 Aula de predicado verbal e nominal - Predicativo do sujeito
8 Aula de predicado verbal e nominal - Predicativo do sujeito
 
Teoria heterotrófica e autotrófica dos primeiros seres vivos..pptx
Teoria heterotrófica e autotrófica dos primeiros seres vivos..pptxTeoria heterotrófica e autotrófica dos primeiros seres vivos..pptx
Teoria heterotrófica e autotrófica dos primeiros seres vivos..pptx
 
O que é arte. Definição de arte. História da arte.
O que é arte. Definição de arte. História da arte.O que é arte. Definição de arte. História da arte.
O que é arte. Definição de arte. História da arte.
 
A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...
A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...
A EDUCAÇÃO FÍSICA NO NOVO ENSINO MÉDIO: IMPLICAÇÕES E TENDÊNCIAS PROMOVIDAS P...
 
6ano variação linguística ensino fundamental.pptx
6ano variação linguística ensino fundamental.pptx6ano variação linguística ensino fundamental.pptx
6ano variação linguística ensino fundamental.pptx
 
Seminário Biologia e desenvolvimento da matrinxa.pptx
Seminário Biologia e desenvolvimento da matrinxa.pptxSeminário Biologia e desenvolvimento da matrinxa.pptx
Seminário Biologia e desenvolvimento da matrinxa.pptx
 
P P P 2024 - *CIEJA Santana / Tucuruvi*
P P P 2024  - *CIEJA Santana / Tucuruvi*P P P 2024  - *CIEJA Santana / Tucuruvi*
P P P 2024 - *CIEJA Santana / Tucuruvi*
 
LENDA DA MANDIOCA - leitura e interpretação
LENDA DA MANDIOCA - leitura e interpretaçãoLENDA DA MANDIOCA - leitura e interpretação
LENDA DA MANDIOCA - leitura e interpretação
 
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdfPROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
 
Currículo - Ícaro Kleisson - Tutor acadêmico.pdf
Currículo - Ícaro Kleisson - Tutor acadêmico.pdfCurrículo - Ícaro Kleisson - Tutor acadêmico.pdf
Currículo - Ícaro Kleisson - Tutor acadêmico.pdf
 
Slide - SAEB. língua portuguesa e matemática
Slide - SAEB. língua portuguesa e matemáticaSlide - SAEB. língua portuguesa e matemática
Slide - SAEB. língua portuguesa e matemática
 
Cartão de crédito e fatura do cartão.pptx
Cartão de crédito e fatura do cartão.pptxCartão de crédito e fatura do cartão.pptx
Cartão de crédito e fatura do cartão.pptx
 

Programação Concorrente - Objetos e Concorrência

  • 1. UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO PROGRAMAÇÃO CONCORRENTE – 2015.1 Fábio M. Pereira (fabio.mpereira@uesb.edu.br)
  • 2. Roteiro • Concorrência • Objetos e Concorrência – Construtores de Execução Concorrente – Concorrência e Programação Orientada a Objetos – Modelos de Objetos e Mapeamento • Forças de Projeto – Segurança – Vivacidade – Performance – Reusabilidade • Padrões de Projeto – Aplicação de Camadas – Adaptadores – Subclasses – Adaptadores de Método
  • 3.
  • 4. Concorrência • Informalmente, um programa concorrente é aquele que faz mais de uma coisa ao mesmo tempo • Por exemplo, um navegador web pode simultaneamente realizar uma requisição HTTP GET para buscar uma página, executar um clipe de áudio, mostrar a quantidade de bytes recebidos de alguma imagem e realizar um diálogo de advertência com o um usuário • Mas às vezes esta simultaneidade é uma ilusão – Em alguns sistemas computacionais estas atividades diferentes podem ser realizadas por diferentes CPUs – Em outros sistemas elas são realizadas por uma única CPU compartilhada que troca entre diferentes atividades tão rápido que elas parecem serem simultâneas para um observador humano
  • 5. Concorrência • Uma definição de programação concorrente mais precisa, porém não muito interessante, pode ser feita operacionalmente: – Uma máquina virtual Java e o sistema operacional fornecem mapeamentos a partir de simultaneidade aparente para paralelismo físico (via múltiplas CPUs) ou, na falta deste, por permitir que atividades independentes sejam realizadas em paralelo quando possível ou desejável, e em outro caso, por compartilhamento de tempo – Programação concorrente consiste no uso de construtores de programação que são mapeados desta maneira – Por convenção, esta noção está restrita a construtores afetando uma única JVM, como oposto à programação distribuída, que, por exemplo, usando remote method invocation (RMI), envolve múltiplas JVMs residindo em múltiplos sistemas computacionais
  • 6. Concorrência • Concorrência e os motivos para seu uso são melhor capturados quando consideramos a natureza de alguns tipos comuns de aplicações concorrentes: – Web services: a maioria dos web services baseados em sockets (por exemplo, daemons HTTP, máquinas servlet e servidores de aplicação) são multithreaded, normalmente, a principal motivação e dar suporte a múltiplas conexões concorrentes para garantir que uma nova solicitação de conexão não precise aguardar a finalização de outras, o que geralmente minimiza a latência e aumenta a disponibilidade – Cálculos numéricos: muitas tarefas intensivas de computação podem ser paralelizadas, e então serem executadas mais rápido se múltiplas CPUs estão presentes, aqui o objetivo é maximizar a saída explorando o paralelismo
  • 7. Concorrência • Continuação... – Processamento I/O: mesmo em um computador nominalmente sequencial, dispositivos que realizam leituras e escritas em discos, redes, etc., operam independentemente da CPU, programas concorrentes podem usar o tempo que outros programas perdem esperando por I/O lento, e podem então fazer uso mais eficiente do recursos de um computador – Simulação: programas concorrentes podem simular objetos físicos com comportamento autônomo independente que são difíceis de realizar em programas puramente sequenciais
  • 8. Concorrência • Continuação... – Aplicações baseadas em GUI: mesmo que a maioria das interfaces do usuário sejam intencionalmente single-threaded, elas geralmente estabelecem ou se comunicam com serviços multithreaded, a concorrência permite que o controle do usuário continue “responsivo” mesmo durante ações que consomem muito tempo – Software baseado em componentes: componentes de software de alta granularidade (por exemplo, aqueles que fornecem ferramentas de projeto, como editores de layout) pode internamente construir threads de maneira a assistir no registro, fornecer suporte multimídia, alcançar maior autonomia ou melhorar a performance
  • 9. Concorrência • Continuação... – Código móvel: frameworks como o pacote java.applet executam código baixado em threads separadas como uma parte de um conjunto de políticas que ajudam a isolar, monitorar e controlar os efeitos de código desconhecido – Sistemas embarcados: a maioria de programas executados em pequenos dispositivos dedicados realizam controle de tempo real, múltiplos componentes estão continuamente reagindo a entradas externas de sensores e outros dispositivos e produzem saídas externas de uma maneira ininterrupta. Todas as implementações JVM dão suporte a controle de tempo real leve, no qual linhas de tempo e performance são considerados tópicos de qualidade de serviço em vez de corretude. Isto reflete objetivos de portabilidade que permitem que a JVM seja implementada em modernos sistemas de hardware e software de multipropósitos
  • 10.
  • 11. Construtores de Execução Concorrente • Threads são apenas um dos vários construtores disponíveis para execução de código concorrente • A ideia de gerar uma nova atividade pode ser mapeada para qualquer uma de várias abstrações que refletem trade-offs de autonomia versus overhead • Projetos baseados em threads nem sempre fornecem a melhor solução para um determinado problema de concorrência • A escolha de uma das alternativas apresentadas a seguir podem fornecer mais ou menos segurança, proteção, tolerância a falhas e controle administrativo, assim como maior ou menor overhead • Diferenças entre estas opções (e seus construtores de programação de suporte associados) impactam em diferentes estratégias de projeto
  • 12. Sistemas Computacionais • Se tivéssemos uma grande oferta de sistemas computacionais, poderíamos mapear cada unidade lógica de execução para um computador diferente • Cada sistema computacional pode ter um único processador, um multiprocessador, ou um conjunto de máquinas administradas como uma única unidade e compartilhando um sistema operacional comum: – Isso proporciona autonomia ilimitada e independência – Cada sistema pode ser administrado separadamente e controlado a partir de todos os outros
  • 13. Sistemas Computacionais • No entanto, a construção, localização, recuperação, e passagem de mensagens entre tais entidades podem ser caros, as oportunidades para compartilhamento de recursos locais são eliminados, e soluções para os problemas em torno de nomes, segurança, tolerância a falhas, recuperação e acessibilidade são todos relativamente pesados em comparação com os observados em programas concorrentes • Portanto, esta escolha de mapeamento é tipicamente aplicado apenas em relação aos aspectos de um sistema que exige uma solução intrinsecamente distribuída • E mesmo dispositivos embarcados de computador ínfimos hospedam mais de um processo
  • 14. Processos • Um processo é uma abstração do sistema operacional que permite que um sistema computacional dê suporte a muitas unidades de execução • Cada processo representa tipicamente um programa em execução em separado; por exemplo, uma execução de JVM • Como a noção de um "sistema computacional", um "processo" é uma abstração lógica, não física, assim, por exemplo, ligações de processos para CPUs podem variar dinamicamente
  • 15. Processos • Os sistemas operacionais garantem algum grau de independência, a ausência de interferências e segurança entre os processos executados concorrentemente • Processos em geral, não têm permissão para acessar locais de armazenamento uns dos outros (embora geralmente existam algumas exceções), e devem, em vez disso, se comunicar via recursos de comunicação entre processos, tais como pipes (tubos) • A maioria dos sistemas fazem pelo menos promessas de melhor esforço sobre como processos serão criados e agendados • Isso quase sempre implica em compartilhamento de tempo preemptivo - a suspensão de processos em uma base periódica para dar a outros processos a chance de executar
  • 16. Processos • A sobrecarga para a criação, gestão e comunicação entre processos pode ser muito menor do que em soluções por máquina • No entanto, uma vez que os processos compartilham recursos computacionais subjacentes (CPUs, memória, canais de E/S), eles são menos autônomos – Por exemplo, um travamento de máquina causado por um processo mata todos os processos
  • 17. Threads • Construtores de threads de várias formas fazem mais trade-offs em autonomia, em parte por causa da menor sobrecarga • Os principais trade-offs são: – Compartilhamento – Agendamento – Comunicação
  • 19. Compartilhamento • Threads podem compartilhar o acesso à memória, arquivos abertos e outros recursos associados a um único processo • Threads na linguagem de programação Java podem compartilhar todos esses recursos • Alguns sistemas operacionais também suportam construções intermediárias, por exemplo "processos leves" e "threads do kernel" que compartilham apenas alguns recursos, fazendo-o apenas mediante pedido explícito, ou impondo outras restrições
  • 20. Agendamento • Garantia de independência pode ser enfraquecida para apoiar políticas de escalonamento mais baratas • Em um extremo, todas as threads podem ser tratadas em conjunto, como um processo single-threaded, caso em que elas podem lidar de forma cooperativa com as outras, de modo que apenas uma thread está sendo executada de cada vez, sem dar a qualquer outra thread a chance de executar até que ela bloqueie • No outro extremo, o agendador subjacente pode permitir que todas as threads em um sistema lidem diretamente umas com as outras através de regras de agendamento preemptivas • Threads na linguagem de programação Java podem ser agendadas usando qualquer política que encontra-se em qualquer lugar entre estes dois extremos
  • 21. Comunicação • Sistemas interagem via comunicação através de canais com ou sem fios, por exemplo, utilizando sockets • Processos também podem se comunicar dessa forma, mas também podem utilizar mecanismos mais leves, como pipes (tubos) e recursos de sinalização entre processos • Threads podem usar todas essas opções, além de outras estratégias mais baratas que dependem de acesso a posições de memória acessíveis por múltiplas threads, e empregando recursos de sincronização baseadas em memória, como bloqueios, espera e os mecanismos de notificação • Estes construtores apoiam a comunicação de maneira mais eficiente, mas algumas vezes incorrem em maior complexidade e, consequentemente, maior potencial de erro de programação
  • 22. Tarefas e Ambientes Leves de Execução • Os trade-offs efetuados em suporte a threads cobrem uma ampla gama de aplicações, mas que nem sempre são perfeitamente compatíveis com as necessidades de uma determinada atividade • Embora os detalhes de desempenho diferem em todas as plataformas, a sobrecarga na criação de uma thread ainda é significativamente maior do que a forma mais barata (mas menos independente) para invocar um bloco de código – chamando-o diretamente na thread atual
  • 23. Tarefas e Ambientes Leves de Execução • Quando a sobrecarga de criação e gerenciamento de threads tornam-se preocupações de desempenho, podemos ser capazes de fazer concessões adicionais de autonomia pela criação de nossos próprios ambientes de execução mais leves, que impõem novas restrições à sua utilização (por exemplo, proibindo o uso de certas formas de bloqueio), ou fazendo menos garantias de agendamento, ou restringindo a sincronização e comunicação a um conjunto mais limitado de opções
  • 24. Tarefas e Ambientes Leves de Execução • Os ambientes leves de execução mais familiares são sistemas baseados em eventos e subsistemas, nos quais as chamadas disparadas de atividades conceitualmente assíncronas são mantidas como eventos que podem ser enfileirados e processados ​​por threads em background • Quando se aplicam, a construção e utilização de tais ambientes podem melhorar tanto a estrutura como o desempenho de programas concorrentes • O seu uso reduz as preocupações que podem de outro modo inibir o uso de técnicas de execução concorrente para expressar atividades logicamente assíncronas e objetos logicamente autônomos
  • 25.
  • 26. Concorrência e Programação OO • Objetos e concorrência estão vinculados, desde os primeiros dias de cada um • A primeira linguagem de programação concorrente OO (criada em cerca de 1966), Simula, também foi a primeira linguagem OO, e foi uma das primeiras linguagens concorrentes • Os construtores iniciais de OO e concorrência de Simula eram um tanto primitivos e estranhos – Por exemplo, a concorrência foi baseado em torno de co-rotinas – construtores baseados em thread que exigiam que programadores explicitamente passassem o controle de uma tarefa para outra
  • 27. Concorrência e Programação OO • Várias outras linguagens que oferecem ambos os construtores, de concorrência e OO, se seguiram – na verdade, até mesmo algumas das versões dos primeiros protótipos de C++ incluíam algumas classes de bibliotecas com suporte à concorrência • E Ada (embora, em suas primeiras versões, dificilmente se parecia com uma linguagem OO) ajudou a trazer programação concorrente para fora do mundo dos sistemas e linguagens de baixo nível especializados
  • 28. Concorrência e Programação OO • Projeto OO não desempenhou nenhum papel prático nos sistemas multithreaded de programação emergentes na década de 1970 • E a concorrência não desempenhou nenhum papel prático na programação OO de larga escala que começou na década de 1980 • Mas o interesse em OO concorrente permaneceu vivo em laboratórios de pesquisa e grupos de desenvolvimento avançados, e ressurgiu como um aspecto essencial da programação, em parte devido à popularidade e onipresença da plataforma Java • Programação OO concorrente compartilha a maioria das características da programação de qualquer tipo, mas difere em aspectos críticos dos tipos de programação que podemos estar mais familiarizados
  • 29. Programação OO Sequencial • Programas OO concorrentes são muitas vezes estruturados utilizando as mesmas técnicas de programação e padrões de projeto como programas OO sequenciais • Mas eles são intrinsecamente mais complexos – quando mais do que uma atividade pode ocorrer ao mesmo tempo, a execução do programa é, necessariamente, não-determinística • O código pode executar em ordem surpreendente – qualquer fim que não seja explicitamente excluído é permitido, então não podemos sempre entender programas concorrentes por leitura sequencial através de seu código
  • 30. Programação OO Sequencial • Por exemplo, sem maiores precauções, um campo definido para um valor em uma linha de código pode ter um valor diferente (devido às ações de alguma outra atividade simultânea) pelo tempo em que a próxima linha de código é executada • Lidar com essa e outras formas de interferência muitas vezes introduz a necessidade de um pouco mais de rigor e uma perspectiva mais conservadora no design
  • 31. Programação Baseada em Eventos • Algumas técnicas de programação concorrente têm muito em comum com as de ambientes de eventos empregadas em kits de ferramentas GUI como o java.awt, javax.swing e em outras linguagens, como Tcl/Tk e Visual Basic • Em ambientes de GUI, eventos como cliques do mouse são encapsulados como objetos Event que são colocados em um único EventQueue • Estes eventos são depois enviados e processados ​​um a um em um único ciclo de eventos, que normalmente é executado como uma thread separada
  • 32. Programação Baseada em Eventos • Este projeto pode ser estendido para dar suporte a concorrência adicional por (entre outras táticas) a criação de várias threads de ciclo de eventos, cada evento processamento concorrentemente, ou até mesmo disparando cada evento em uma thread separada • Novamente, isso abre novas possibilidades de projeto, mas também introduz novas preocupações sobre interferência e coordenação entre as atividades concorrentes
  • 33. Programação de Sistemas Concorrentes • Programação concorrente orientada a objetos difere de programação de sistemas multithreaded em linguagens como C, devido, principalmente, ao encapsulamento, modularidade, extensibilidade, segurança e recursos de segurança que faltam em C • Além disso, suporte a concorrência é construído na linguagem de programação Java, e não fornecido por bibliotecas • Isso elimina a possibilidade de alguns erros comuns, e também permite que os compiladores automaticamente e com segurança executem algumas otimizações que precisam ser executadas manualmente em C
  • 34. Outras Linguagens de Programação Concorrente • Essencialmente, todas as linguagens de programação concorrente são, em algum nível, equivalentes, se considerarmos apenas o sentido de todas as linguagens concorrentes, acreditamos não termos definido as características de concorrência certas • No entanto, não é tão difícil fazer programas em uma linguagem parecerem quase equivalentes aos de outras linguagens ou aquelas que utilizam outros construtores, através do desenvolvimento de pacotes, classes, utilitários, ferramentas e convenções de codificação que imitam recursos incorporados em outras
  • 35.
  • 36. Modelos de Objetos e Mapeamentos • Concepções de objetos muitas vezes diferem entre programação OO sequencial e concorrente, e mesmo entre diferentes estilos de programação OO concorrente • Contemplação dos modelos de objetos e mapeamentos subjacentes pode revelar a natureza das diferenças entre os estilos de programação sugeridos na seção anterior • A maioria das pessoas gosta de pensar em objetos de software como modelos de objetos reais, representados com algum grau arbitrário de precisão • A noção de "real" é, naturalmente, aos olhos de quem vê, e muitas vezes inclui artifícios que só fazem sentido dentro do reino da computação
  • 37. Exemplo • Considerando o diagrama de classes UML e o esboço de código para a classe WaterTank: class Watertank {// esboço de código final float capacity; float currentVolume = 0.0f; WaterTank overflow; WaterTank(float cap) { capacity = cap; ... } void addWater(float amount) throws OverflowException; void removeWater(float amount) throws UnderflowException; }
  • 38. Exemplo • A intenção aqui é representar, ou simular, um tanque de água com: – Atributos, tais como capacity e currentVolume, que são representados como campos de objetos Watertank – Podemos escolher somente os atributos que nos preocupemos em algum contexto de uso, por exemplo, enquanto todos os tanques de água reais têm locais, formas, cores e assim por diante, esta classe só lida com volumes – Restrições de estado invariantes, como os fatos de que o currentVolume permanece sempre entre zero e capacity, e que capacity não seja negativo e nunca mude após a construção
  • 39. Exemplo – Operações que descrevem comportamentos, como aqueles para addWater e removeWater – Esta escolha de operações reflete novamente algumas decisões de design implícitas relativas a exatidão, granularidade e precisão – Por exemplo, poderíamos ter escolhido para modelar os reservatórios de água ao nível das válvulas e interruptores, e poderíamos ter modelado cada molécula de água como um objeto que muda de localização como o resultado das operações associadas
  • 40. Exemplo – Conexões (e conexões potenciais) para outros objetos com os quais objetos se comunicam, tais como tubos ou outros tanques – Por exemplo, o excesso de água encontrado numa operação addWater pode ser desviado para um tanque de transbordamento que é conhecido por cada tanque – Pré-condições e pós-condições sobre os efeitos das operações, como as regras segundo as quais é impossível remover a água de um tanque vazio, ou adicionar água para um tanque cheio que não esteja equipado com um tanque de transbordamento disponível – Restrições de Protocolos quando e como mensagens (os pedidos de operação) são processados – Por exemplo, podemos impor uma regra de que no máximo uma mensagem de addWater ou removeWater é processada em um determinado momento ou, em alternativa, uma regra afirmando que as mensagens removeWater são permitidos no meio de operações addWater
  • 41. Modelos de Objetos • A classe Watertank usa objetos para modelar a realidade • Os modelos de objetos fornecem regras e ambientes para a definição de objetos de forma mais geral, que abrange: – Estado – a estrutura de cada objeto é descrito (normalmente através de uma classe), em termos de atributos internos (estado), conexões com outros objetos, métodos locais (internos), e métodos ou portas para aceitar mensagens de outros objetos – Encapsulamento - os objetos têm membranas que separam o seu interior do exterior, o estado interno pode ser diretamente modificado apenas pelo próprio objeto
  • 42. Modelos de Objetos – Comunicação - objetos se comunicam apenas através de passagem de mensagem, mensagens de objetos disparam ações em outros objetos – As formas dessas mensagens podem variar de chamadas de procedimentos simples até aqueles transportados via protocolos arbitrários de comunicação – Identidade - novos objetos podem ser construídos em qualquer momento (sujeito a restrições de recursos do sistema) por qualquer objeto (sujeito a controle de acesso), uma vez construído, cada objeto mantém uma identidade única que persiste ao longo de sua vida
  • 43. Modelos de Objetos – Conexões – um objeto pode enviar mensagens para os outros, se ele conhece suas identidades – Alguns modelos contam com identidades de canal em vez de ou em adição a identidades de objetos – Abstratamente, um canal é um veículo para a passagem de mensagens – Dois objetos que compartilham um canal podem passar mensagens através desse canal, sem saber as identidades de cada um – Linguagens e modelos OO típicos se baseiam em primitivas de objeto para chamada direta de métodos, abstrações baseadas em canal para ES e comunicação através de rede, e construções, tais como canais de eventos que podem ser vistos a partir de qualquer perspectiva
  • 44. Modelos de Objetos – Computação - objetos podem realizar quatro tipos básicos de computação: • Aceitar uma mensagem • Atualizar estado interno • Enviar uma mensagem • Criar um novo objeto • Esta caracterização abstrata pode ser interpretada e refinada de várias maneiras: – Por exemplo, uma forma de implementar um objeto Watertank é a construção de um pequeno dispositivo de hardware de propósito especial que só mantém os estados indicados, instruções e conexões – Mas desde que este não é um curso sobre design de hardware, vamos ignorar tais opções e restringir a atenção para alternativas baseadas em software
  • 45. Mapeamento Sequencial • As características de um computador comum de uso geral (CPU, um canal de comunicação, alguma memória, e algumas portas IO) podem ser exploradas de modo a que este computador pode fingir que é qualquer objeto, por exemplo, um Watertank • Isso pode ser realizado ao carregar uma descrição do reservatórios de água (através de um arquivo .class) em uma JVM • A JVM pode, então, construir uma representação passiva de uma instância e, em seguida, interpretar as operações associadas • Esta estratégia de mapeamento também se aplica ao nível da CPU quando as operações são compilados em código nativo em vez de interpretadas como bytecodes • Ela também se estende aos programas que incluam muitos objetos de classes diferentes, cada um carregado e instanciado, conforme necessário, fazendo com que a JVM em todos os momentos registre a identidade ("this") do objeto que está simulando
  • 46. Mapeamento Sequencial JVM Sequencial Interpretador interpret() { ... } IO Representação de um objeto WaterTank Representação de uma classe WaterTank Estado: contador do programa, endereços de objetos
  • 47. Mapeamento Sequencial • Em outras palavras, a JVM é em si um objeto, embora um muito especial que pode fingir que é outro objeto (mais formalmente, ela serve como uma Máquina Universal de Turing) • Enquanto estados semelhantes valem para os mapeamentos utilizados na maioria das outras linguagens, objetos Class e reflexão tornam mais simples caracterizar objetos reflexivos que tratam outros objetos como dados • Em um ambiente puramente sequencial, isto seria o fim da história
  • 48. Mapeamento Sequencial • Mas considerando as restrições sobre o modelo de objetos genérico imposto por este mapeamento, em uma JVM sequencial seria impossível simular diretamente múltiplos objetos waterTank interagindo concorrentemente • E uma vez que toda passagem de mensagem é realizada via invocação de procedimento sequencial, não há necessidade de regras sobre se várias mensagens podem ser processadas concorrentemente • Assim, o processamento OO sequencial limita os tipos de conceitos de projeto de alto nível que são permitidos expressar
  • 49. Objetos Ativos • No outro extremo do espectro de mapeamento estão modelos de objetos ativos (também conhecidos como modelos de ator), em que cada objeto é autônomo • Cada um pode ser tão poderoso como uma JVM sequencial • Classes internas e representações de objetos podem ter as mesmas formas como as usadas ​​em estruturas passivas – Por exemplo, aqui, cada waterTank poderia ser mapeado para um objeto ativo separado, pelo carregamento em uma descrição de uma JVM separada, e, em seguida, sempre permitindo simular as ações definidas
  • 50. Objetos Ativos • Modelos de objeto ativos formam uma visão de alto nível comum de objetos em sistemas orientados a objetos distribuídos: diferentes objetos podem residir em máquinas diferentes, por isso o local e domínio administrativo de um objeto são muitas vezes questões importantes de programação • Toda a passagem de mensagens é organizada através de comunicação remota (por exemplo, através de sockets) que podem obedecer a qualquer de uma série de protocolos, incluindo: – mensagens de sentido único (mensagens que não intrinsecamente requerem respostas) – multicasts (envio simultaneamente da mesma mensagem para vários destinatários) e – estilo procedimental de trocas de solicitação-resposta
  • 51. Objetos Ativos • Este modelo também serve como uma visão orientada a objetos da maioria dos processos de nível do sistema operacional, cada um dos quais é tão independente de (e compartilham poucos recursos com) outros processos quanto possível
  • 52. Objetos Ativos um objeto ativo atributos, conexões accept message anAction() { update state send messages create objects } mensagem
  • 53. Modelos Mistos • Os modelos e mapeamentos subjacentes dão suporte a concorrência na linguagem de programação Java e está entre os dois extremos: dos modelos passivos e ativos • Uma JVM completa pode ser composta de vários threads, cada uma das quais atua da mesma maneira que uma única JVM sequencial, no entanto, ao contrário dos objetos ativos puros, todos essas threads podem compartilhar o acesso ao mesmo conjunto de representações passivas subjacentes • Este estilo de mapeamento pode simular cada um dos extremos
  • 54. Modelos Mistos • Modelos puramente sequenciais passivos podem ser programados usando apenas uma única thread • Modelos puramente ativos podem ser programados através da criação de tantas threads quanto objetos ativos existentes, evitando situações em que mais de uma thread possa acessar uma determinada representação passiva, e usando construtores que fornecem os mesmos efeitos semânticos que passagem de mensagens remotas, no entanto, a maioria dos programas concorrentes ocupam uma posição intermediária
  • 55. Modelos Mistos • Modelos OO concorrentes baseados em threads conceitualmente separam objetos passivos "normais" de objetos ativos (threads) • Mas os objetos passivos geralmente exibem consciência- thread não vista na programação sequencial, por exemplo, pela proteção através de bloqueios • E os objetos ativos são mais simples do que os observados em modelos de ator, dando suporte a apenas algumas operações (como run)
  • 56. Modelos Mistos • Mas o projeto de sistemas OO concorrentes pode ser abordado a partir de qualquer uma destas duas direções: – por objetos passivos conscientizados de existirem em um ambiente multithread, – ou por “emburrecimento” de objetos ativos para que eles possam ser expressos mais facilmente usando construtores de thread
  • 57. Modelos Mistos uma Thread run() { ... } estado da thread uma Thread estado da thread run() { ... } uma Thread estado da thread run() { ... } Representação de um objeto WaterTank Representação de uma classe WaterTank
  • 58. Modelos Mistos • Uma das razões para apoiar este tipo de modelo de objetos é que ele mapeia de forma simples e eficiente o armazenamento em hardware e sistemas operacionais em processador único e em multi-processamento de memória compartilhada (SMP): – threads podem ser vinculadas a CPUs quando possível e desejável, e de outra forma a tempo-compartilhado – o estado de threads locais são mapeadas para registradores e CPUs – e representações de objetos compartilhados mapeados para a memória principal compartilhada
  • 59. Modelos Mistos CPU cache de registradores CPU cache de registradores bus IO memória principal uma representação de objeto
  • 60. Modelos Mistos • O grau de controle do programador sobre esses mapeamentos é uma distinção que separa muitas formas de programação paralela de programação concorrente • Programação paralela clássica envolve etapas de projeto explícito para mapear threads, tarefas ou processos, bem como de dados, para processadores físicos e seus locais de armazenamento • Programação concorrente deixa a maioria das decisões de mapeamento para a JVM (e o sistema operacional subjacente), isto melhora a portabilidade, às custas da necessidade de acomodar as diferenças na qualidade de implementação destes mapeamentos
  • 61. Modelos Mistos • Compartilhamento de tempo é realizado mediante a aplicação do mesmo tipo de estratégia de mapeamento para as próprias threads: representações de objetos Thread são mantidas, e um agendador organiza trocas de contexto em que o estado da CPU correspondente a uma thread é guardado em sua representação de armazenamento associada e restaurado a partir de outra • Vários refinamentos e extensões de tais modelos e mapeamentos são possíveis – Por exemplo, as aplicações e os sistemas de objetos persistentes normalmente contam com bases de dados para manter representações de objetos em vez de depender diretamente na memória principal
  • 62.
  • 63. Forças de Projeto • Investigaremos preocupações de design que surgem no desenvolvimento de software concorrente • A maioria das construções e padrões de projeto apresentadas mais adiante incluem descrições de como eles resolvem forças aplicáveis ​​discutidas aqui (assim como outras que são menos diretamente vinculadas à concorrência, como a precisão, a realização de testes e assim por diante)
  • 64. Forças de Projeto • Podemos tomar duas visões complementares de qualquer sistema OO, centrada em objeto e centrada em atividade: Sistema = Objetos + Atividades • Sob uma visão centrada em objeto, um sistema é uma coleção de objetos interligados, mas é uma coleção estruturada, não uma sopa de objetos aleatórios • Objetos se aglomeram em grupos, por exemplo, o grupo de objetos compreendendo uma ParticleApplet, formando assim componentes e subsistemas maiores
  • 65. Forças de Projeto • Sob uma visão centrada em atividade, um sistema é um conjunto de atividades possivelmente simultâneas • Em um nível mais refinado, estes são apenas mensagens individuais enviadas (normalmente, chamadas de métodos) • Eles, por sua vez se organizam em conjuntos de cadeias de chamadas, sequências de eventos, tarefas, sessões, transações e threads • Uma atividade lógica, como executar o ParticleApplet, pode envolver muitas threads • Em um nível superior, algumas dessas atividades representam casos de uso de todo o sistema
  • 66. Forças de Projeto • Nenhuma visão sozinha fornece uma imagem completa de um sistema, uma vez que um determinado objeto pode estar envolvido em várias atividades, e, inversamente, uma determinada atividade pode se estender por vários objetos • No entanto, essas duas visões dão origem a dois conjuntos complementares de preocupações com correção, uma centrada em objeto e outra centrada em atividade: – Segurança - nada de ruim acontece a um objeto – Vivacidade - algo eventualmente acontece dentro de uma atividade
  • 67. Forças de Projeto • Falhas de segurança levam a um comportamento não intencional em tempo de execução - as coisas começam a acontecer errado • Falhas em vivacidade podem levar a nenhum comportamento - as coisas param de acontecer • Infelizmente, algumas das coisas mais simples que podemos fazer para melhorar as propriedades de vivacidade podem destruir propriedades de segurança, e vice-versa • Torná-los ao mesmo tempo corretos pode ser um desafio • Devemos equilibrar os efeitos relativos dos diferentes tipos de falha em nossos programas
  • 68. Forças de Projeto • Mas é uma engenharia padrão (não apenas de engenharia de software) a prática de colocar ênfase no projeto preliminar sobre a segurança • Quanto mais o seu código realmente importa, o melhor é garantir que um programa não faça nada que possa levar a um comportamento aleatório, até mesmo perigoso • Por outro lado, a maioria do tempo gasto em ajustes em projetos de concorrência na prática geralmente envolve questões de vivacidade e de eficiência relacionadas à vivacidade
  • 69. Forças de Projeto • E as vezes há boas razões para sacrificar seletivamente a segurança pela vivacidade – Por exemplo, pode ser aceitável para apresentações visuais mostrar transitoriamente um total absurdo de execuções concorrentes descoordenadas - desenhar pixels isolados, indicadores incorretos de progresso ou imagens que não têm relação com suas formas intencionais - se estivermos confiantes de que este estado de coisas vai logo ser corrigido
  • 70. Forças de Projeto • Questões de segurança e vivacidade poderão ser prorrogadas para abranger duas categorias de preocupações de qualidade, uma principalmente centrada em objeto e outra principalmente centrada em vivacidade, que também estão, por vezes, em oposição direta: – Reutilização - a utilidade de objetos e classes em múltiplos contextos – Performance - o grau em que as atividades executam logo e rapidamente
  • 71.
  • 72. Segurança • Práticas de programação concorrente seguras são generalizações de práticas de programação sequenciais seguras e protegidas • Segurança em projetos concorrentes acrescenta uma dimensão temporal às noções comuns de segurança de tipo • Um programa com checagem de tipo pode não estar correto, mas pelo menos ele não faz coisas perigosas como interpretar mal os bits que representam um float como se fossem uma referência de objeto • Da mesma forma, um projeto concorrente seguro pode não ter o efeito pretendido, mas pelo menos ele nunca encontra erros devido à corrupção das representações, por conter threads
  • 73. Segurança • Uma diferença prática entre segurança de tipo e segurança multithreaded é que a maioria dos tópicos de segurança de tipo podem ser verificados automaticamente por compiladores • Um programa que não consegue passar pela checagem em tempo de compilação não pode sequer ser executado • A maioria das questões de segurança multithreaded, no entanto, não podem ser verificadas automaticamente, e por isso deve contar com a disciplina do programador • As técnicas para garantir a segurança descritas aqui dependem de práticas cuidadosas de engenharia (incluindo vários com raízes em formalismos) em vez de os próprios métodos formais
  • 74. Segurança • Segurança multithreaded também adiciona uma dimensão temporal para o projeto e técnicas de programação em torno da segurança • Práticas de programação segura desabilitam o acesso a determinadas operações nos objetos e recursos a partir de certos invocadores ou aplicações • Controle de concorrência introduz desativação temporária de acesso com base no exame das ações em curso realizadas por outras threads • O principal objetivo na preservação da segurança é garantir que todos os objetos em um sistema mantenham estados consistentes: estados em que todos os campos, e todos os campos de outros objetos dos quais dependem, possuam valores legais, significativos
  • 75. Segurança • Às vezes preciso muito trabalho para determinar exatamente o que "legal" e o que "significativo" representam em uma classe particular • Um caminho é primeiro estabelecer invariantes de nível conceitual, por exemplo, a regra de que os volumes de tanques de água devem estar sempre entre zero e as suas capacidades • Estas geralmente podem ser reformulados em termos de relações entre os valores de campos nas classes concretas associadas • Um objeto é consistente se todos os campos obedecem seus invariantes • Cada método público em cada classe deve levar um objeto de um estado consistente para outro
  • 76. Segurança • Objetos seguros podem ocasionalmente entrar em estados transitoriamente inconsistentes no meio de métodos, mas nunca tentar iniciar novas ações quando estão em estados inconsistentes • Se cada objeto é projetado para executar ações somente quando é logicamente capaz de fazê-lo, e se toda a mecânica está devidamente implementada, então podemos ter certeza de que um aplicativo usando esses objetos não vai encontrar quaisquer erros devido a inconsistência do objeto
  • 77. Segurança • Uma das razões para ser mais cuidadoso sobre invariantes em programas concorrentes é a de que é muito mais fácil quebrá- las inadvertidamente que na maioria dos programas sequenciais • A necessidade de proteção contra os efeitos da inconsistência surge mesmo em contextos sequenciais, por exemplo, quando processando exceções e retornos de chamada e ao fazer auto- chamadas a partir de um método em uma classe para outro, no entanto, estas questões tornam-se muito mais centrais em programas concorrentes • As formas mais comuns para garantir consistência empregam técnicas de exclusão para garantir a atomicidade de ações públicas – que cada ação seja executada até o fim, sem interferência de outros
  • 78. Segurança • Sem essa proteção, inconsistências em programas concorrentes podem resultar de condições de corrida produzindo conflitos de armazenamento no nível de células de memória: – Conflitos de leitura/gravação - uma thread lê um valor de um campo enquanto outra escreve nele, o valor visto pela thread de leitura é difícil de prever, já que depende de qual thread ganhou a "corrida" para acessar o campo pela primeira vez, o valor lido não precisa nem mesmo ser um valor que já foi escrito por qualquer thread – Conflitos escrita/escrita - duas threads tentam escrever no mesmo campo, o valor visto na próxima leitura é novamente difícil ou impossível de prever
  • 79. Segurança • É igualmente impossível prever as consequências das ações que são tentadas quando os objetos estão em estados inconsistentes • Os exemplos incluem: – Uma representação gráfica (por exemplo de uma partícula) é apresentada num local que o objeto nunca efetivamente ocupou – Um saldo de conta bancária está incorreto após uma tentativa de retirar o dinheiro no meio de uma transferência automática – Seguir o próximo ponteiro de uma lista ligada leva a um nó que ainda não está na lista – Duas atualizações simultâneas de sensores fazem com que um controlador de tempo real realize uma ação com efeito incorreto
  • 80. Atributos e Restrições • Técnicas de programação segura confiam na compreensão clara das propriedades e restrições necessárias que cercam representações de objetos • Os desenvolvedores que não estão cientes dessas propriedades raramente fazem um trabalho muito bom em preservá-los • Muitos formalismos estão disponíveis para precisamente representar predicados que descrevem requisitos – Estes podem ser muito úteis, mas aqui vamos manter a precisão suficiente sem a introdução de formalismos
  • 81. Atributos e Restrições • Requisitos de consistência, por vezes, resultam de definições conceituais de alto nível de atributos realizadas durante o projeto inicial de classes • Estas restrições normalmente existem independentemente de como os atributos são concretamente representados e acessados via campos e métodos • Isto foi visto, por exemplo, no desenvolvimento da classe Watertank
  • 82. Atributos e Restrições • Aqui estão alguns outros exemplos: – Uma ContaBancaria tem um saldo que é igual à soma de todos os depósitos e retiradas menos juros e taxas de serviço – Um Pacote tem um destino que deve ser um endereço IP legal – Um Contador tem um valor de contagem integral não-negativo – Uma Fatura tem um pagamento devido que reflete as regras de um sistema de pagamento – Um Termostato tem uma temperatura igual à leitura mais recente do sensor – Uma Figura tem uma localização, dimensão e cor que obedecem um conjunto de diretrizes de estilo para um determinado conjunto de ferramentas GUI
  • 83. Atributos e Restrições • Aqui estão alguns outros exemplos (continuação): – Um BufferLimitado tem um contadorDeElementos que está sempre entre zero e uma capacidade – Uma Pilha tem um tamanho e, quando não estiver vazia, um elemento de topo – Uma Janela tem um conjuntoDePropriedades que mantém mapeamentos atuais de fontes, cor de fundo, etc. – Um Intervalo tem uma dataInicial que não seja posterior a sua dataFinal
  • 84. Atributos e Restrições • Enquanto tais atributos essencialmente sempre de alguma maneira são mapeados em campos de objetos, as correspondências não precisam ser diretas • Por exemplo, o topo de uma Pilha não é normalmente armazenado em uma variável, mas, em vez disso em um elemento de uma matriz ou nó de lista ligada • Além disso, alguns atributos podem ser calculados ("derivados") através de outros, por exemplo, o atributo booleano descoberto de uma ContaBancaria pode ser calculado através da comparação do saldo com zero
  • 85. Restrições Representacionais • Outras restrições e invariantes que são feitas para uma determinada classe normalmente surgem como decisões de implementação adicionais • Campos declarados em prol da manutenção de uma estrutura de dados particular, para melhorar o desempenho, ou para outros fins de controle interno, muitas vezes precisam respeitar um conjuntos de invariantes
  • 86. Restrições Representacionais • Campos e restrições podem ser classificados em diversas categorias, como apresentado a seguir • Representações de valor direto - os campos necessários para implementar atributos concretos, por exemplo, um buffer pode ter um campo putIndex mantendo a posição do índice do array, usado para inserir o próximo elemento adicionado • Representações de valor armazenado em cache - campos usados ​​para eliminar ou minimizar a necessidade de cálculos ou invocações de método, por exemplo, em vez de calcular o valor de descoberto cada vez que for necessário, uma ContaBancaria pode manter um campo descoberto que é verdadeiro se, e somente se, o saldo atual é inferior a zero
  • 87. Restrições Representacionais • Representações de estado lógico - reflexões do estado de controle lógico, por exemplo, um BankCardReader pode ter um campo cartão que representa o cartão que está sendo lido, e um campo validPIN que registra se o código de acesso PIN foi verificado, o campo validPIN do CardReader pode ser usado para controlar o ponto em um protocolo em que o cartão foi lido com sucesso e então validado – Algumas representações de estado assumem a forma de variáveis ​​de regras, controlando respostas a todo um conjunto de métodos relacionados (às vezes aquelas declaradas em uma única interface), por exemplo, um objeto de jogo pode alternar entre papéis ativo e passivo, dependendo do valor de um campo whoseTurn
  • 88. Restrições Representacionais • Variáveis ​​de estado de execução - campos que registram o estado dinâmico de um objeto, por exemplo, o fato de que uma determinada operação está em andamento – Variáveis ​​de estado de execução podem representar o fato de que uma determinada mensagem foi recebida, que a ação correspondente foi iniciada, que a ação tenha terminado e que uma resposta à mensagem foi emitida – Uma variável de estado de execução é muitas vezes um tipo enumerado com os valores representando o estado, por exemplo, CONECTANDO, ATUALIZANDO, EMESPERA – Outro tipo comum de variável de estado de execução é um contador que registra o número de entradas ou saídas de algum método – Objetos em programas concorrentes tendem a requerer mais variáveis ​​do que aqueles em contextos sequenciais, para ajudar a controlar e gerenciar o progresso dos métodos que executam de forma assíncrona
  • 89. Restrições Representacionais • Variáveis ​​de história - representações da história ou estados passados ​​de um objeto – A representação mais ampla é um log da história, registrando todas as mensagens já recebidas e enviadas, juntamente com todas as ações internas correspondentes a mudanças de estado que tenham sido iniciadas ou concluídas – Subconjuntos menos extensos são muito mais comuns, por exemplo, uma classe BankAccount poderia manter um campo lastSavedBalance que mantém o último valor de checkpoint e poderia ser utilizado para reverter operações canceladas
  • 90. Restrições Representacionais • Variáveis ​​de controle de versão - um inteiro, marcador de tempo, objeto de referência, código de assinatura, ou outra representação indicando o tempo, ordem, ou a natureza da última mudança de estado feito por um objeto, por exemplo, um Termostato pode incrementar um numeroDeLeitura ou gravar o ultimoHorarioDeLeitura quando atualizar sua Temperatura • Referências a conhecidos - campos apontando para outros objetos que o host interage, mas que não restrigem-se ao estado lógico do host, por exemplo, um alvo de callback de um EventDispatcher, ou um requestHandler delegado por um WebServer
  • 91. Restrições Representacionais • Referências a objetos de representação - os atributos que são conceitualmente mantidos por um objeto host, mas que são, na verdade, gerenciados por outros objetos auxiliares – Campos de referência podem apontar para outros objetos que auxiliam na representação do estado do objeto host – Assim, o estado lógico de qualquer objeto pode incluir os estados de objetos que ele possui referência – Além disso, os próprios campos de referência fazem parte do estado concreto do objeto host – Todas as tentativas para garantir a segurança devem levar esses relacionamentos em consideração
  • 92. Restrições Representacionais • Referências a objetos de representação (exemplos): – Uma Pilha pode ter um campo headOfLinkedList para manter o primeiro nó de uma lista que representa a pilha – Um objeto Pessoa pode manter um campo homePageUrl mantido como um objeto java.net.URL – O saldo de um BankAccount pode ser mantido em um repositório central, caso em que o BankAccount poderia manter um campo com referência ao repositório (a fim de consultar o saldo atual), neste caso, uma parte do estado lógico do BankAccount na realidade é gerenciado pelo repositório
  • 93.
  • 94. Vivacidade • As preocupações de segurança devem ser equilibradas por preocupações de vivacidade • Algumas propriedades "liveness" podem ser construídas como propriedades de segurança de conjuntos de objetos thread • Por exemplo, o deadlock-freedom pode ser definido evitando- se o mau estado no qual um conjunto de threads infinitamente esperam umas pelas outras • Em sistemas “vivos”, todas as atividades eventualmente progridem em direção à conclusão, cada método chamado eventualmente é executado, mas uma atividade pode (talvez apenas transitoriamente) não progredir por qualquer uma das várias razões inter-relacionadas apresentadas a seguir
  • 95. Vivacidade • Bloqueio (locking) – um método sincronizado bloqueia uma thread porque outra thread mantém o bloqueio • Esperar (waiting) - um método bloqueia (via Object.wait ou seus derivados) à espera de um evento, mensagem, ou condição que ainda tem de ser produzida dentro de outra thread • Entrada (input)- um método baseado em IO espera por uma entrada que ainda não chegou a partir de um outro processo ou dispositivo • Contenção de CPU - uma thread não consegue executar mesmo que esteja em um estado executável porque outras threads, ou mesmo programas completamente separados em execução no mesmo computador, estão ocupando CPU ou outros recursos computacionais
  • 96. Vivacidade • Falha (failure) - um método executando em uma thread encontra uma exceção prematura, erro ou falha • Bloqueios momentâneos em threads em progresso são geralmente aceitáveis, na verdade, bloqueios frequentes de curta duração é intrínseco a muitos estilos de programação concorrente • O ciclo de vida típico de uma thread pode incluir um número de bloqueios transientes e reescalonamentos, no entanto, a falta permanente ou ilimitada de progresso é geralmente um problema sério
  • 97. Ciclo de Vida Típico de uma Thread Criada Pronta para execução Em execução Bloqueada Concluída início agendamento bloqueiodesbloqueio retorno, falha
  • 98. Vivacidade • Exemplos de falhas liveness potencialmente permanentes incluem: – Impasse (deadlock) - dependências circulares entre bloqueios, no caso mais comum, thread A mantém um bloqueio para o objeto X e, em seguida, tenta conseguir um bloqueio para o objeto Y; simultaneamente, a thread B já mantém um bloqueio para o objeto Y e tenta conseguir um bloqueio para o objeto X; nenhuma das threads podem mais fazer progressos – Sinais perdidos (missed signals) – uma thread permanece dormente porque começou a esperar depois que uma notificação para despertá-la foi produzida
  • 99. Vivacidade • Exemplos de falhas liveness potencialmente permanentes incluem (continuação): – Bloqueios de monitoramento aninhados - uma thread em espera mantém um bloqueio que poderia ser necessário a qualquer outra thread que tente despertá-la – Livelock - uma ação contínua de nova tentativa (retry), continuamente falha – Inanição (starvation) - a JVM/SO falha, nunca alocando tempo de CPU para uma thread, o que pode acontecer devido a políticas de agendamento ou mesmo ataques hostis de negação de serviços, no computador host
  • 100. Vivacidade • Exemplos de falhas liveness potencialmente permanentes incluem (continuação): – Esgotamento de recursos - um grupo de threads em conjunto detêm todos de um número finito de recursos, uma delas necessita de recursos adicionais, mas nenhuma thread irá desistir em favor de outra – Falha Distribuída - uma máquina remota conectada por um socket servindo como um InputStream falha ou se torna inacessível
  • 101.
  • 102. Performance • Forças baseadas no desempenho estendem conceitos de vivacidade • Além de exigir que cada método invocado, eventualmente, execute, metas de desempenho os obrigam a executar breve e de maneira rápida • Embora não consideraremos sistemas de tempo real hard, em que falhas de execução dentro de um determinado intervalo de tempo podem levar a erros catastróficos do sistema, quase todos os programas concorrentes têm metas de desempenho implícitas ou explícitas
  • 103. Performance • Requisitos de desempenho significativos são expressos em termos de qualidades mensuráveis, incluindo as métricas apresentadas a seguir • O objetivo pode ser expresso em uma tendência central (por exemplo, média, mediana) das medições, assim como em sua variabilidade (por exemplo, faixa de valores, desvio padrão)
  • 104. Medidas de Desempenho • Rendimento (throughput) - o número de operações realizadas por unidade de tempo, as operações de interesse podem variar de métodos individuais até a execução inteira do programa – Na maioria das vezes, o rendimento não é relatado como uma taxa, mas em vez disso, como o tempo levado para executar uma operação • Latência - o tempo decorrido entre a emissão de uma mensagem (via por exemplo, um clique do mouse, a invocação de método, ou conexão de socket de entrada) e de sua realização – Em contextos onde as operações são uniformes, single-threaded, e "continuamente" solicitadas, a latência é apenas o inverso do rendimento – Mas, mais tipicamente, as latências de interesse refletem os tempos de resposta - os atrasos até que algo aconteça, não necessariamente a conclusão integral de um método ou serviço
  • 105. Medidas de Desempenho • Capacidade - o número de atividades simultâneas que podem ser realizadas por um determinado alvo em um rendimento mínimo ou latência máxima – Especialmente em aplicações de rede, isso pode servir como um indicador útil de disponibilidade geral, uma vez que reflete o número de clientes que podem ser atendidos sem deixar a conexões cair devido a time-outs ou sobrecargas da fila da rede • Eficiência - rendimento dividido pela quantidade de recursos computacionais (por exemplo, CPUs, memória e dispositivos IO) necessários para obter esse rendimento
  • 106. Medidas de Desempenho • Escalabilidade - a taxa à qual a latência ou o rendimento melhora quando os recursos (novamente, geralmente CPUs, memória, ou dispositivos) são adicionados a um sistema – Medidas relacionadas incluem utilização – o percentual de recursos disponíveis que são aplicados a uma tarefa de interesse • Degradação - a taxa em que a latência ou o rendimento piora à medida que mais clientes, atividades ou operações são adicionados sem a adição de recursos
  • 107. Performance • A maioria dos projetos multithreaded aceitam implicitamente um pequeno trade-off de eficiência computacional ruim para obter uma melhor latência e escalabilidade • Suporte a concorrência introduz os tipos de sobrecarga e de contenção apresentados a seguir, que podem tornar programas lentos
  • 108. Sobrecarga e Contenção • Bloqueios - um método sincronizado normalmente requer uma maior sobrecarga de chamada do que um método não sincronizado, além disso, os métodos que frequentemente bloqueiam esperam pelos bloqueios (ou por qualquer outro motivo), sendo mais lentos do que aqueles que não o fazem • Monitores - Object.wait, Object.notify, Object.notifyAll, e os métodos deles derivados (como Thread.join) podem ser mais custosos do que outras operações de suporte a tempo de execução da JVM básica • Threads - criar e iniciar uma thread é tipicamente mais caro do que a criação de um objeto comum e chamar um método dele
  • 109. Sobrecarga e Contenção • Mudança de contexto (context-switching) - o mapeamento de threads para CPUs encontra uma sobrecarga de mudança de contexto quando a JVM/OS salva o estado da CPU associado a uma thread, seleciona outra thread a ser executada, e carrega o estado da CPU associado • Agendamento - cálculos e políticas subjacentes que selecionam qual thread está elegíveis para executar adiciona uma sobrecarga, estas podem ainda interagir com outras tarefas do sistema, tais como processamento assíncrono de eventos e coleta de lixo
  • 110. Sobrecarga e Contenção • Localidade - em multiprocessadores, quando várias threads estão em execução em diferentes CPUs compartilhando o acesso aos mesmos objetos, hardware de consistência de cache e software de sistema de baixo nível devem comunicar os valores associados entre os processadores
  • 111. Sobrecarga e Contenção • Algoritmos - alguns algoritmos sequenciais eficientes não se aplicam em ambientes concorrentes – Por exemplo, algumas estruturas de dados que dependem de rotinas de cache onde apenas se sabe que exatamente uma tread executa todas as operações – No entanto, também existem algoritmos concorrentes alternativos eficientes para muitos problemas, incluindo aqueles que criam a possibilidade de novos aumentos de velocidade através do paralelismo • As sobrecargas gerais associadas com construtores de concorrência diminuem de forma constante com a melhora das JVMs
  • 112.
  • 113. Reusabilidade • Uma classe ou objeto é reutilizável na medida em que ela pode ser facilmente empregada em diferentes contextos, tanto como um componente de caixa preta ou como a base de extensão de caixa branca através de subclasses e técnicas relacionadas • A interação entre as preocupações de segurança e vivacidade podem afetar significativamente a capacidade de reutilização • Geralmente é possível projetar componentes para serem seguros em todos os contextos possíveis – Por exemplo, um método sincronizado que se recusa a iniciar até que ele possua o bloqueio de sincronização irá fazer isso, não importa como ele é usado
  • 114. Reusabilidade • Mas em alguns desses contextos, programas usando esse componente seguro pode encontrar falhas de vivacidade (por exemplo, o deadlock) • Por outro lado, a funcionalidade em torno de um componente utilizando apenas métodos não sincronizados será sempre “vivo” (pelo menos no que diz respeito ao bloqueio), mas podem encontrar violações de segurança quando múltiplas execuções concorrentes ocorrem
  • 115. Reusabilidade • As dualidades de segurança e vivacidade são refletidas em alguns pontos de vista extremos de metodologia de projeto: – Algumas estratégias de projeto top-down tomam uma abordagem de segurança pura em primeiro lugar: certifica-se de que cada classe e objeto é seguro, e, posteriormente, tenta melhorar a vivacidade como uma medida de otimização – Em oposto, a abordagem bottom-up é muitas vezes adotada em programação de sistemas concorrentes: certifica-se de que o código é executado, e, em seguida, tenta dividi-lo em camadas de recursos de segurança, por exemplo, adicionando bloqueios • Nenhum dos extremos é especialmente bem sucedido na prática: é muito fácil que abordagens top-down resultem em sistemas lentos, propensos a impasses, e que abordagens bottom-up resultem em código com erros, com violações de segurança não previstas
  • 116. Reusabilidade • Geralmente é mais produtivo prosseguir com o entendimento de que alguns componentes muito úteis e eficientes não são, e não precisam ser, absolutamente seguros, e que serviços úteis realizados por alguns componentes não são absolutamente “vivos” • Em vez disso, eles operam corretamente somente em determinados contextos de uso restrito • Portanto, estabelecer, documentar, publicar e explorar esses contextos tornam-se questões centrais no projeto de software concorrente
  • 117. Reusabilidade • Existem duas abordagens gerais (e uma gama de opções intermédias) para lidar com a dependência de contexto: – (1) minimizar a incerteza “fechando” partes de sistemas, e – (2) estabelecer políticas e protocolos que permitam que os componentes se tornem ou permaneçam abertos • Muitos esforços práticos de design envolvem um pouco de cada
  • 119. Subsistemas Fechados • Um sistema fechado ideal é aquele para o qual temos de maneira perfeita e estática (tempo de projeto) o conhecimento sobre todos os comportamentos possíveis • Isto é tipicamente tanto inatingível como indesejável, no entanto, muitas vezes é possível fechar partes de sistemas, em unidades individuais que variam a partir de classes de componentes a nível de produto, através do emprego de versões possivelmente extremas de técnicas de encapsulamento OO apresentadas a seguir
  • 120. Técnicas de Encapsulamento OO • Comunicação externa restrita - todas as interações, tanto internas como externas, ocorrem através de uma interface reduzida, no caso mais tratável, o subsistema é fechado à comunicação, nunca internamente invocando métodos em objetos fora do subsistema • Estrutura interna determinística - a natureza concreta de todos os objetos e threads que compõem o subsistema é estaticamente conhecida, as palavras-chave final e private podem ser usadas para ajudar a impor isso
  • 121. Subsistemas Fechados • Em pelo menos alguns desses sistemas, podemos, em princípio, provar - informalmente, formalmente, ou até mesmo mecanicamente - que não são possíveis violações de segurança interna ou de vivacidade, dentro de um componente fechado • Ou, se elas são possíveis, podemos continuar a refinar projetos e implementações até que um componente esteja provavelmente correto • No melhor dos casos, podemos, então, aplicar esse conhecimento de composição para analisar outras partes de um sistema que contam com este componente
  • 122. Subsistemas Fechados • Informações estáticas perfeitas sobre objetos, threads e interações nos dizem não só o que pode acontecer, mas também o que não pode acontecer – Por exemplo, pode ser o caso que, embora dois métodos sincronizados em dois objetos contenham chamadas um para o outro, eles nunca podem ser acessados simultaneamente por diferentes threads dentro do subsistema, desse modo o impasse nunca ocorrerá
  • 123. Subsistemas Fechados • Fechamento também pode apresentar novas oportunidades de otimização manual ou dirigida pelo compilador – Por exemplo, a remoção de sincronização de métodos que normalmente poderiam requere-lo, ou empregar algoritmos inteligentes de propósito especial que podem ser desenvolvidos para aplicação apenas pela eliminação de possibilidade de interação indesejada – Sistemas embarcados são muitas vezes compostos como coleções de módulos fechados, em parte para melhorar a previsibilidade, escalonabilidade e performance
  • 124. Subsistemas Fechados • Embora os subsistemas fechados sejam tratáveis, eles também podem ser frágeis • Quando as restrições e premissas que governam a sua estrutura interna mudam, esses componentes são muitas vezes descartados e reconstruídos a partir do zero
  • 125. Subsistemas Abertos • Um sistema aberto ideal é infinitamente extensível, através de várias dimensões: – Pode carregar classes desconhecidas de forma dinâmica, permitir que subclasses substituam qualquer método, empregar callbacks entre objetos de diferentes subsistemas, compartilhar recursos comuns entre threads, usar reflexão para descobrir e invocar métodos em objetos de outra maneira desconhecidos, e assim por diante • Abertura ilimitada é geralmente tão inatingível e indesejável quanto fechamento completo: se tudo pode mudar, então não podemos programar nada • Mas a maioria dos sistemas exigem, pelo menos, alguma dessa flexibilidade
  • 126. Subsistemas Abertos • Análise estática completa de sistemas abertos não é possível ainda, uma vez que a sua natureza e estrutura evoluem ao longo do tempo • Em vez disso, os sistemas abertos devem contar com políticas e protocolos documentados a que cada componente adere • A Internet é um dos melhores exemplos de um sistema aberto: – Ela continuamente evolui, por exemplo, adicionando novos hospedeiros, páginas web, e serviços, exigindo apenas que todos os participantes obedeçam a algumas políticas e protocolos de rede – Tal como acontece com outros sistemas abertos, a aderência às políticas e protocolos da Internet é, por vezes, difícil de aplicar – No entanto, JVMs se organizam de forma que os componentes com não-conformidade não podem catastroficamente danificar a integridade do sistema
  • 127. Subsistemas Abertos • Projeto orientado por políticas pode funcionar bem em nível muito menor de sistemas concorrentes típicos, onde as políticas e protocolos muitas vezes tomam a forma de regras de projeto • Exemplos de domínios de políticas incluem: – Fluxo - por exemplo, uma regra na forma: componentes do tipo A enviam mensagens para os do tipo B, mas nunca vice-versa – Bloqueio - por exemplo, uma regra na forma: métodos do tipo A sempre disparam exceções imediatamente se o recurso R não está disponível, em vez de bloquear até que esteja disponível – Notificações - por exemplo, uma regra na forma: Objetos do tipo A sempre enviam notificações de mudança aos seus ouvintes (listeners), sempre que atualizados
  • 128. Subsistemas Abertos • Adopção de um número relativamente pequeno de políticas simplifica o projeto, minimizando a possibilidade de decisões inconsistentes caso a caso • Autores de componentes, talvez com a ajuda de revisores de código e ferramentas, precisam checar apenas se eles estão obedecendo as regras de projeto relevantes, e de outra forma podem focar a atenção nas tarefas em mãos, desenvolvedores podem pensar localmente, enquanto ainda agindo globalmente
  • 129. Subsistemas Abertos • No entanto, o design orientado a política pode se tornar incontrolável quando o número de políticas cresce muito e as obrigações de programação que elas induzem sobrecarregam desenvolvedores • Quando os métodos, mesmo simples, como atualização de um saldo de conta ou impressão "Olá, mundo" exigem dezenas de linhas de código estranho propenso a erros de conformidade com políticas de projeto, é hora de tomar algum tipo de ação de reparação: – Simplificar ou reduzir o número de políticas – Criar ferramentas que ajudam a automatizar a geração de código e/ou a verificação de conformidade – Criar linguagens específicas de domínio que impõem uma determinada disciplina; ou – Criar ambientes e bibliotecas que reduzam a necessidade de que tanto código de suporte tenha que ser escrito dentro de cada método
  • 130. Subsistemas Abertos • Enquanto induzir maior fechamento nos permite otimizar o desempenho, realizar uma maior abertura permite que otimizemos para mudanças futuras • Estes dois tipos de afinações e refatorações são muitas vezes igualmente desafiadoras, mas têm efeitos opostos: – Otimizar para desempenho geralmente implica em explorar casos especiais por decisões de projeto hard-wired (conexão direta permanente) – Otimizar para extensibilidade implica na remoção de decisões hard-wired permitindo a variação, por exemplo, através de encapsulamento através de métodos substituíveis, ou abstração da funcionalidade através de interfaces que podem ser re- implementadas de formas completamente diferentes por componentes carregados dinamicamente
  • 131. Subsistemas Abertos • Porque programas concorrentes tendem a incluir mais decisões políticas do que os programas sequenciais, e tendem a depender mais pesadamente de invariantes envolvendo escolhas particulares de representação, classes que envolvem construtores de concorrência muitas vezes acabam por exigir uma atenção especial, a fim de serem facilmente extensíveis • Este fenômeno é muito comum, o suficiente para ter um nome especial, chamado anomalia de herança
  • 132. Subsistemas Abertos • No entanto, algumas outras técnicas de programação desnecessariamente restringem extensibilidade por razões de desempenho • Essas táticas se tornam mais questionáveis em melhora de compiladores e JVMs • Por exemplo, a compilação dinâmica permite que muitos componentes extensíveis sejam tratados como se eles fossem fechados no momento de carregamento da classe, levando a otimizações e especializações que exploram determinados contextos de tempo de execução de forma mais eficaz do que qualquer programador poderia
  • 133. Documentação • Quando a composicionalidade é dependente do contexto, é vital para o uso pretendido, que contextos e restrições envolvendo componentes sejam bem compreendidos e bem documentados • Quando esta informação não é fornecida, utilização, reutilização, manutenção, testes, gerenciamento de configuração, a evolução do sistema e as preocupações de engenharia de software relacionadas se tornam muito mais difíceis
  • 134. Documentação • A documentação pode ser usada para melhorar a compreensibilidade por qualquer um dos vários públicos: outros desenvolvedores usando uma classe como um componente de caixa-preta, autores de subclasses, os desenvolvedores que mais tarde irão manter, modificar ou reparar o código, testadores e revisores de código, e usuários do sistema • Através destas audiências, o primeiro objetivo é o de eliminar a necessidade de uma documentação extensa, minimizando o inesperado, e, portanto, reduzindo a complexidade conceitual via: padronização, clareza e código auxiliar
  • 135. Padronização • Usando políticas, protocolos e interfaces comuns • Por exemplo: – A adoção de padrões de projeto, referenciando livros, páginas da web ou documentos de projeto que os descrevem de maneira mais plena – Empregando bibliotecas utilitárias padrão e frameworks – Usando idiomas de codificação padrão e convenções de nomes – Realizando buscas através de listas de revisão padrão que enumeram erros comuns
  • 136. Clareza • Utilizando as expressões de código mais simples e em sua maioria autoexplicativas • Por exemplo: – Utilizar exceções para advertir sobre condições verificadas – Expressando restrições internas via qualificadores de acesso (como private) – Adoção de padrões comuns de nomenclatura e assinatura, por exemplo, determinação de que, salvo indicação em contrário, os métodos que podem bloquear declaram que eles lançam uma InterruptedException
  • 137. Código Auxiliar • Fornecimento de código que demonstra usos pretendidos • Por exemplo: – Incluindo amostras ou exemplos de uso recomendadas – Fornecimento de trechos de código que alcançam efeitos não- óbvios – Incluindo métodos projetados a servir como auto testes
  • 138. Documentação • Após eliminar a necessidade de explicações óbvias via documentação, formas mais úteis de documentação podem ser usadas para esclarecer decisões de projeto • Os detalhes mais críticos podem ser expressos de forma sistemática, usando anotações semiformais mostradas na tabela a seguir
  • 139. Anotações Semiformais PRE Pré-condição (não necessariamente checada) /** PRE: Caller holds synch lock … WHEN Condição de guarda (sempre checada) /** WHEN: not empty return oldest … POST Pós-condição (geralmente não checada) /** POST: Resource r is released … OUT Garantia de envio de mensagem (por exemplo callback) /** OUT: c.process(buff) called after read … RELY Propriedade requerida (geralmente não checada) de outros objetos e métodos /** RELY: Must be awakened by x.signal() … INV Uma restrição de objeto verdadeira no início e no final de cada método público /** INV: x,y are valid screen coordinates … INIT Uma restrição de objeto que deve ser mantida na construção /** INIT: bufferCapacity greater than zero …
  • 140. Documentação • Documentação adicional, menos estruturada pode ser usada para explicar as restrições não-óbvias, limitações contextuais, suposições e decisões de design que impactem o uso em um ambiente concorrente • É impossível fornecer uma lista completa de construções que necessitam desse tipo de documentação, mas casos típicos incluem: – Informações de design de alto nível sobre o estado e as restrições de métodos – Limitações de segurança conhecidas devido à falta de bloqueio em situações que exigem isso – O fato de um método poder bloquear indefinidamente à espera de uma condição, evento ou recurso – Métodos destinados a serem chamados apenas a partir de outros métodos, talvez em outras classes
  • 141.
  • 142. Padrões de Projeto • Muitos projetos concorrentes são melhor descritos como padrões • Um padrão encapsula uma forma de projeto comum e bem sucedida, geralmente uma estrutura de objeto (também conhecida como um microarquitetura) que consiste em uma ou mais interfaces, classes e/ou objetos que obedecem a certas restrições e relacionamentos estáticos e dinâmicos • Padrões são um veículo ideal para a caracterização de desenhos e técnicas que não precisam ser implementadas exatamente da mesma maneira em diferentes contextos, e, portanto, não podem ser encapsuladas de maneira útil como componentes reutilizáveis
  • 143. Padrões de Projeto • Os componentes reutilizáveis e frameworks podem desempenhar um papel central no desenvolvimento de software concorrente, mas grande parte da programação OO concorrente implica em reutilização, adaptação e extensão das recorrentes formas e práticas de projeto e não de classes particulares
  • 144. Aplicação de Camadas • Estratificação de controle de políticas sobre mecanismos é um princípio estruturante comum em sistemas de todos os tipos • Muitas técnicas de estratificação e de composição OO contam com “imprensar” alguma chamada de método ou corpo de código entre ações antes e depois • Todas as formas de controle antes/depois providenciam que um determinado método seja interceptado por isso, sempre executam a sequência: before(); method(); after(); • Ou, para assegurar que ações depois sejam realizadas mesmo se os métodos centrais encontrarem exceções: before(); try { method(); } finally { after(); }
  • 145. Aplicação de Camadas • Por exemplo, um método sincronized adquire um bloqueio antes de executar o código dentro do método, e libera o bloqueio após o método ser concluído • Mas as ideias básicas de padrões antes/depois podem ser ilustradas em conjunto com outra prática útil na programação OO, código de autocontrole: – Os campos de qualquer objeto devem preservar todas as invariantes sempre que o objeto não está envolvido em um método público • Invariantes devem ser mantidas mesmo que esses métodos lancem qualquer uma das suas exceções declaradas, a menos que essas exceções denotem corrupção ou falha do programa (como pode ocorrer em RuntimeExceptions e Errors )
  • 146. Aplicação de Camadas • Conformidade com as invariantes computáveis ​​pode ser testada de forma dinâmica através da criação de classes que as verificam tanto sobre a entrada como na saída de cada método público • Técnicas semelhantes aplicam-se a pré-condições e pós- condições, mas para simplificar, vamos ilustrar apenas com invariantes • Como exemplo, suponha que nós gostaríamos de criar classes de tanque de água que contêm um autocontrole sobre a invariante que o volume esteja sempre entre zero e a capacidade • Para fazer isso, podemos definir um método checkVolumeInvariant e usá-lo tanto antes como depois da operação
  • 147. Aplicação de Camadas • Podemos definir primeiro uma exceção para lançar se a invariante falhar: class AssertionError extends java.lang.Error { public AssertionError() { super(); } public AssertionError(String message) { super(message); } } • Pode ser perturbador inserir essas verificações manualmente dentro de cada método • Em vez disso, um dos três padrões de projeto antes/depois pode ser usado para separar os controles dos métodos base: classes de adaptadores, projetos baseados em subclasses, e classes adaptadoras de métodos
  • 148. Aplicação de Camadas • Em todos os casos, a melhor maneira de configuração é a definição de uma interface que descreva a funcionalidade básica • Interfaces são quase sempre necessárias quando precisamos dar espaço suficiente para variação nas implementações • Por outro lado, a falta de interfaces existentes limita as opções quando retroativamente aplicamos padrões antes/depois
  • 149. Aplicação de Camadas • Aqui está uma interface descrevendo uma variante menor da classe tanque de água, técnicas antes/depois podem ser aplicadas para verificar invariantes em torno da operação transferWater: interface Tank { float getCapacity(); float getVolume(); void transferWater(float amount) throws OverflowException, UnderflowException; }
  • 150. Adaptadores • Quando interfaces padronizadas são definidas depois do projeto de uma ou mais classes concretas, essas classes, muitas vezes não implementam a interface desejada, por exemplo, os nomes de seus métodos podem ser ligeiramente diferentes dos definidos na interface • Se não podemos modificar essas classes concretas para corrigir esses problemas, podemos obter o efeito desejado através da construção de uma classe Adapter que traduz as incompatibilidades
  • 151. Adaptadores • Digamos que tenhamos uma classe Performer que possui o método perform e cumpre todas as qualificações para ser usada como um Runnable exceto pelo desencontro de nomes • É possível construir um Adapter para que ela possa ser usada em uma thread por alguma outra classe:
  • 152. Adaptadores class AdaptedPerformer implements Runnable { private final Performer adaptee; public AdaptedPerformer(Performer p) { adaptee = p; } public void run() { adaptee.perform(); } } • Este é apenas um dos muitos contextos comuns para construção de adaptadores, que também formam a base de vários padrões relacionados apresentados no livro Design Patterns – Um Proxy é um adaptador com a mesma interface que o seu delegado – Um Composite mantém uma coleção de delegados, todos dando suporte à mesma interface
  • 153. Adaptadores • Neste estilo baseado em delegação de composição, a classe host de acesso público delega todos os métodos para um ou mais delegados e recebe de volta respostas, talvez fazendo alguma tradução (mudanças de nome, conversão de parâmetros, filtragem de resultados, etc.) que cerca as chamadas aos delegados • Os adaptadores podem ser usados ​​para fornecer controle antes/depois meramente envolvendo a chamada delegada dentro das ações de controle
  • 154. Adaptadores • Por exemplo, supondo que temos uma classe de implementação, chamada TankImpl , podemos escrever a seguinte classe AdaptedTank, que pode ser usada em vez da original em alguma aplicação, substituindo todas as ocorrências de: new TankImpl(...) • Por: new AdaptedTank(new TankImpl(...))
  • 155. AdaptedTank class AdaptedTank implements Tank { protected final Tank delegate; public AdaptedTank(Tank t) { delegate = t; } public float getCapacity() { return delegate.getCapacity(); } public float getVolume() { return delegate.getVolume(); } protected void checkVolumeInvariant() throws AssertionError { float v = getVolume(); float c = getCapacity(); if ( !(v >= 0.0 && v <= c) ) throw new AssertionError(); }
  • 156. AdaptedTank public synchronized void transferWater( float amount) throws OverflowException, UnderflowException { checkVolumeInvariant(); // before-check try { delegate.transferWater(amount); } // The re-throws will be postponed until // after-check in the finally clause catch (OverflowException ex) { throw ex; } catch (UnderflowException ex) { throw ex; } finally { checkVolumeInvariant(); // after-check } } }
  • 157. Subclasses • No caso normal, quando as versões dos métodos interceptados antes/depois têm os mesmos nomes e usos das versões de base, subclasse pode ser uma alternativa mais simples do que a utilização de adaptadores • Versões subclasse de métodos podem interpor verificações em torno de chamadas para suas versões super
  • 158. Exemplo de Subclasses class SubclassedTank extends TankImpl { protected void checkVolumeInvariant() throws AssertionError { // ... identical to AdaptedTank version ... } public synchronized void transferWater( float amount) throws OverflowException, UnderflowException { // identical to AdaptedTank version except // for inner call: // ... try { super.transferWater(amount); } // ... } }
  • 160. Subclasses X Adaptadores • Algumas escolhas entre subclasses e adaptadores são apenas uma questão de estilo, outras refletem diferenças entre delegação e herança • Adaptadores permitem manipulações que escapam às regras de subclassificação, por exemplo, não podemos substituir um método público como privado em uma subclasse, a fim de desabilitar o acesso, mas podemos simplesmente deixar de delegar o método em um adaptador • Várias formas de delegação podem até ser usadas como um substituto de tipos para subclassificação fazendo com que cada classe "sub" (Adapter) mantenha uma referência a uma instância de sua classe "super" (Adaptee), delegando todas as operações "herdadas"
  • 161. Subclasses X Adaptadores • Delegação também pode ser mais flexível do que subclassificação, uma vez que objetos "sub" podem até mesmo mudar seus "supers" dinamicamente (através da redefinição da referência delegada) • Delegação pode também ser usada para obter os efeitos de herança múltipla, por exemplo, se uma classe deve implementar duas interfaces independentes, chamadas Tank e java.awt.event.ActionListener, e há duas superclasses disponíveis que fornecem a funcionalidade necessária, então uma destas pode ser uma subclasse e o outro delegado
  • 162. Subclasses X Adaptadores • No entanto, a delegação é menos poderosa do que subclassificação em alguns outros aspectos • Por exemplo, auto-chamadas em "superclasses" não são automaticamente vinculadas às versões dos métodos que foram “sobrescritos” em “subclasses” baseadas em delegação • Projetos de adaptadores também podem cair em problemas que giram em torno do fato de que os objetos Adaptee e Adapter são objetos diferentes, por exemplo, testes de igualdade de referência a objetos devem ser realizados com mais cuidado uma vez que um teste para ver se temos a versão Adaptee de um objeto falha se temos a versão Adapter, e vice-versa
  • 163. Métodos de Modelo • Quando estamos bastante certos de que iremos utilizar um controle antes/depois em um conjunto de classes relacionadas, podemos criar uma classe abstrata que automatiza a sequência de controle através de uma aplicação do padrão Template Method (que não tem nada a ver com tipos genéricos C++)
  • 165. Métodos de Modelo • Uma classe abstrata que dá suporte a métodos de modelo estabelece um ambiente que facilita a construção de subclasses que podem substituir as ações base, métodos antes/depois, ou ambos: – Código de ação no nível básico é definido em métodos não- públicos (por convenção, chamamos a versão não-pública de qualquer método method como doMethod), um pouco menos flexíveis, estes métodos não precisam ser declaradas não- públicos se forem concebidos para ser sobrescritos em subclasses – Operações antes e depois também são definidas como métodos não-públicos – Métodos públicos invocam os métodos base entre os métodos antes e depois
  • 166. Métodos de Modelo • Aplicando ao exemplo Tank: abstract class AbstractTank implements Tank { protected void checkVolumeInvariant() throws AssertionError { // ... identical to AdaptedTank version ... } protected abstract void doTransferWater(float amount) throws OverflowException, UnderflowException; public synchronized void transferWater(float amount) throws OverflowException, UnderflowException { // identical to AdaptedTank version except // for inner call: // ... try { doTransferWater(amount); } // ... } }
  • 167. Métodos de Modelo • Aplicando ao exemplo Tank (continuação): class ConcreteTank extends AbstractTank { protected final float capacity; protected float volume; // ... public float getVolume() { return volume; } public float getCapacity() { return capacity;} protected void doTransferWater(float amount) throws OverflowException, UnderflowException { // ... implementation code ... } }
  • 168. Adaptadores de Método • A abordagem mais flexível, mas, por vezes, mais difícil de controle antes/depois é definir uma classe cujo propósito é o de chamar um método específico em um determinado objeto • No padrão Command Object e em suas várias versões, as instâncias dessas classes podem então ser passadas, manipuladas, e, finalmente, executadas (aqui, entre as operações antes/depois)
  • 169. Adaptadores de Método • Por causa das regras de tipagem estática, deve haver um tipo diferente de classe do adaptador para cada tipo de método que está sendo adaptado • Para evitar a proliferação de todos esses tipos, a maioria das aplicações restringem a atenção para apenas um ou um pequeno conjunto de interfaces genéricas, cada uma definindo um único método • Por exemplo, a classe Thread e a maioria dos outros frameworks de execução aceitam apenas instâncias da interface Runnable para invocar seus métodos run, contendo argumentos, resultado e exceções • Da mesma forma, definimos e usamos a interface Callable contendo apenas um método call que aceita um argumento Object, retorna um Object , e pode lançar qualquer Exception
  • 170. Adaptadores de Método • Podemos aplicar uma versão de antes/depois de divisão em camadas com base nos adaptadores de método primeiro pela definição de uma interface TankOp: interface TankOp { void op() throws OverflowException, UnderflowException; } • No código de exemplo apresentado a seguir, estranhamente, todas as utilizações de adaptadores de método são locais para uma classe TankWithMethodAdapter • Além disso, neste pequeno exemplo, há apenas um método adaptável
  • 171. Adaptadores de Método • No entanto, o mesmo processo poderia ser utilizado para quaisquer outros métodos de Tank definidos nesta classe ou em suas subclasses • Adaptadores de método são muito mais comuns em aplicações onde instâncias devem ser registradas e/ou transmitidas entre vários objetos antes de serem executadas, o que justifica os custos extras de instalação e obrigações de programação
  • 172. Adaptadores de Método class TankWithMethodAdapter { // ... protected void checkVolumeInvariant() throws AssertionError { // ... identical to AdaptedTank version ... } protected void runWithinBeforeAfterChecks (TankOp cmd) throws OverflowException, UnderflowException { // identical to AdaptedTank.transferWater // except for inner call: // ... try { cmd.op(); } // ... }
  • 173. Adaptadores de Método protected void doTransferWater(float amount) throws OverflowException, UnderflowException { // ... implementation code ... } public synchronized void transferWater(final float amount) throws OverflowException, UnderflowException { runWithinBeforeAfterChecks(new TankOp() { public void op() throws OverflowException, UnderflowException { doTransferWater(amount); } }); } }
  • 174. Adaptadores de Método • Algumas aplicações de adaptadores de método podem ser parcialmente automatizadas através de recursos de reflexão • Um construtor genérico pode sondar uma classe para um determinado java.lang.reflect.Method, configurar argumentos, invoca-lo, e transferir de volta resultados • Isto tem o preço de maior sobrecarga, bem como a necessidade de lidar com as muitas exceções que podem surgir • Portanto, geralmente só vale a pena quando se lida com código desconhecido carregado dinamicamente
  • 175. UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO PROGRAMAÇÃO CONCORRENTE – 2015.1 Fábio M. Pereira (fabio.mpereira@uesb.edu.br)