programação concorrente segura em java · mento das threads em alto nível de abstração, que é...
TRANSCRIPT
João Vitor Mallmann
Programação Concorrente Segura em Java
Orientador:
José Mazzucco Júnior
UNIVERSIDADE FEDERAL DE SANTA CATARINA
Florianópolis
28 de Janeiro de 2007
List of Algorithms
1 Canal estendendo um linkdriver. . . . . . . . . . . . . . . . . . . . . . . . . p. 24
2 Exemplo de uma classe que implementa a interface de Processo. . . . . . . . p. 25
3 Criando e executando um processo. . . . . . . . . . . . . . . . . . . . . . . p. 26
4 Construtor de composição seqüencial. . . . . . . . . . . . . . . . . . . . . . p. 27
5 Exemplo de uma composição seqüencial. . . . . . . . . . . . . . . . . . . . p. 28
6 Adição de um novo processo a composição seqüencial. . . . . . . . . . . . . p. 28
7 Adição de vários novos processos a composição seqüencial. . . . . . . . . . p. 28
8 Inserção de processo em determinada posição na composição seqüencial . . . p. 28
9 Remoção de processos da composição seqüencial . . . . . . . . . . . . . . . p. 28
10 Construtor de composição Paralelo. . . . . . . . . . . . . . . . . . . . . . . p. 29
11 Exemplo de uma composição Paralela. . . . . . . . . . . . . . . . . . . . . . p. 29
12 Adição de um novo processo a composição Paralela. . . . . . . . . . . . . . p. 29
13 Adição de vários novos processos a composição Paralela. . . . . . . . . . . . p. 29
14 Remoção de processos da composição seqüencial. . . . . . . . . . . . . . . . p. 30
15 Construtor de composição Paralelo com prioridade. . . . . . . . . . . . . . . p. 30
16 Exemplo de uma composição Paralelo com prioridade. . . . . . . . . . . . . p. 31
17 Exemplo de uma construção aninhada da composição Paralela com priori-
dade, aumentando os níveis de prioridade. . . . . . . . . . . . . . . . . . . . p. 32
18 Adição de um novo processo a composição Paralela com prioridade. . . . . . p. 32
19 Adição de vários novos processos a composição Paralela com prioridade. . . p. 32
20 Inserção de processo em determinada posição na composição Paralela com
prioridade. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 33
21 Remoção de processos da composição Paralela com prioridade. . . . . . . . . p. 33
22 Construtor de composição Alternativo. . . . . . . . . . . . . . . . . . . . . . p. 33
23 Exemplo de uma composição Alternativa. . . . . . . . . . . . . . . . . . . . p. 33
24 Inserção de um novo Guard na composição Alternativa. . . . . . . . . . . . . p. 34
25 Inserção de vários Guard na composição Alternativa. . . . . . . . . . . . . . p. 34
26 Remoção de Guard da composição Alternativa. . . . . . . . . . . . . . . . . p. 34
27 Declaração de um objeto da classe PriAlternative. . . . . . . . . . . . . . . . p. 34
28 Exemplo de uma composição Alternativa com prioridade, . . . . . . . . . . . p. 35
29 Adição de um novo guarda a composição PriAlternative. . . . . . . . . . . . p. 35
30 Adição de vários guardas a composição PriAlternative. . . . . . . . . . . . . p. 35
31 Adição de vários guardas a composição PriAlternative. . . . . . . . . . . . . p. 36
32 Adição de vários guardas a composição PriAlternative. . . . . . . . . . . . . p. 36
33 Exemplo de composição aninhada, composição paralela com duas composições
seqüenciais aninhadas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 36
34 Criando um objeto da classe Guard. . . . . . . . . . . . . . . . . . . . . . . p. 38
35 Execução condicional de um Guard. . . . . . . . . . . . . . . . . . . . . . . p. 38
36 Declaração de um Guarda condicional. . . . . . . . . . . . . . . . . . . . . . p. 39
37 Declaração de um Guarda incondicional. . . . . . . . . . . . . . . . . . . . . p. 39
38 Parte 1 da classe Mesa, da aplicação Jantar dos filósofos implementada em
CTJ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 46
39 Parte 2 da classe Mesa, da aplicação Jantar dos filósofos implementada em
CTJ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 47
40 Parte 3 da classe Mesa, da aplicação Jantar dos filósofos implementada em
CTJ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 48
41 Classe Filosofo, da aplicação Jantar dos filósofos implementada em CTJ. . . . p. 49
42 Classe JantarDosFilosofos, da aplicação Jantar dos filósofos implementada
em CTJ. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 50
43 Classe Mesa, da aplicação Jantar dos filósofos implementada em Java. . . . . p. 51
44 Classe Filosofo, da aplicação Jantar dos filósofos implementada em Java. . . p. 52
Sumário
Lista de Figuras
1 Objetivo p. 8
1.1 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 8
1.2 Limitações do tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 8
1.3 Objetivo geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 9
1.4 Motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 9
1.5 Desenvolvimento do trabalho . . . . . . . . . . . . . . . . . . . . . . . . . p. 9
2 Introdução p. 10
3 Communicating Sequencial Process (CSP) p. 12
4 Diferenças entre threads de Java e processos CSP p. 14
5 Primitivas de sincronização p. 16
6 Estados de transição p. 18
6.1 Estados de transição da thread. . . . . . . . . . . . . . . . . . . . . . . . . . p. 18
6.2 Estados de transição de um processo. . . . . . . . . . . . . . . . . . . . . . p. 19
7 Processos CTJ p. 21
8 Canal CTJ p. 22
8.1 Independente de hardware: . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 23
8.2 Dependente de hardware: . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 23
9 Utilizando CTJ p. 25
9.1 Criando processos em Java . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 25
10 Composição de processos p. 27
10.1 Construtor de Composição Seqüencial - Sequencial . . . . . . . . . . . . . p. 27
10.2 Construtor de Composição Paralelo - Parallel . . . . . . . . . . . . . . . . . p. 29
10.3 Construtor de Composição Paralelo baseado em Prioridade - PriParallel . . . p. 30
10.4 Construtor de Composição Alternativo - Alternative . . . . . . . . . . . . . p. 33
10.5 Construtor de Composição Alternativo baseado em Prioridade - PriAlternative p. 34
10.6 Construtor de Composição Aninhados . . . . . . . . . . . . . . . . . . . . . p. 36
11 Guardas Condicionais e Incondicionais p. 38
12 Validação do programas CSP p. 40
13 Comparação entre aplicações utlizando CTJ e Java p. 42
14 Conclusão p. 45
Referências p. 53
Lista de Figuras
1 Diagrama de transição de estados das threads. . . . . . . . . . . . . . . . . . p. 19
2 Diagrama de transição de estados dos processos (HILDERINK et al., 1997). . . . p. 20
3 Interface de processo (HILDERINK et al., 1997). . . . . . . . . . . . . . . . . . p. 21
4 Interface de canal CTJ (HILDERINK et al., 1997). . . . . . . . . . . . . . . . . p. 23
5 Arquitetura do calculo de verificação (KLEBANOV et al., 2005). . . . . . . . . p. 41
6 Rede de processos do Jantar dos Filósofos. . . . . . . . . . . . . . . . . . . . p. 43
8
1 Objetivo
1.1 Tema
A criação de sistemas de tempo real está cada vez mais em evidência, sendo necessário para
isso à utilização de softwares que possam agir de forma concorrente, utilizando-se o conceito
de threads pra obter-la.
Com a liberdade e flexibilidade dada pelas APIs, o desenvolvimento de aplicações uti-
lizando processamento multithread ficou simplificado, resultando em sistemas defeituosos, de-
vido ao perigo da ocorrência de condições indesejadas, tais como corridas de hazards, deadlock,
livelock e starvation (negação de serviços infinita).
Desta forma, deve se tomar cuidado com cada tipo de sincronismo utilizado nas threads de
uma aplicação, utilizando um conjunto de regras, normas e padrões de projeto.
1.2 Limitações do tema
No trabalho a ser desenvolvido será o usado o pacote Communicating Threads for Java
(CTJ), threads que se comunicam em Java, que implementa o modelo CSP em Java, que inclui
padrões de threads muito mais simples e confiáveis que o modelo das threads de Java. O pacote
CTJ fornece um pequeno conjunto de padrões de projeto que é suficiente para programação
concorrente em Java. Um importante avanço deste pacote é que o programador pode usar um
grande conjunto de regras para eliminar os hazardas, deadlocks, livelock, starvation etc. durante
o projeto e implementação.
9
A teoria de processos seqüenciais de comunicação (Communicating Sequential Process -
CSP) oferece uma notação matemática pra descrever padrões de comunicação por expressões
algébricas que contém evidências formais para análise, verificando e eliminando as condições
indesejáveis na utilização de threads. Esse modelo foi provado bem-sucedido para criação de
softwares concorrentes para tempo real e sistemas embarcados.
1.3 Objetivo geral
Desenvolver um problema computacionai clássicos utilizando Java e a teoria de CSP, fazendo
comparações entre a mesma aplicação utilizando apenas Java, verificando a viabilidade da uti-
lização da teoria CSP aliado a Java.
1.4 Motivação
Devido a grande disseminação da linguagem Java, e sua vasta área de atuação, verificar
se a utilização do pacote CTJ, que implementa a teoria de CSP, elimina o grande problema
na utilização de múltiplas threads, que é a geração de hazardas, deadlocks, livelock, starvation
etc. Pretendendo difundir a teoria de CSP, sua funcionalidade na prática, e os custos para a sua
utilização na resolução de problemas computacionais.
1.5 Desenvolvimento do trabalho
Pesquisa e buscas de referências na literatura para o estudo da teoria de CSP, de Charles
Antony Richard Hoare.
Pesquisa e busca de referências na literatura por pacotes que implementam a teoria de CSP
em Java.
Desenvolvimento de aplicações utilizando o modelo de CSP para Java, para comparações
com códigos que utilizam multithread em Java.
10
2 Introdução
Apesar da implementação de aplicações multithread em Java não ser difícil, a sua utilização
de forma correta requer muito esforço. O raciocínio sobre aplicações utilizando threads, pode
ser diferente para cada programa construído. Threads são as principais causas no aumento na
complexidade das aplicações concorrentes. Como resultado, programas multithread são muito
mais suscetíveis a erros.
A sincronização e o escalonamento de Java são mal especificados, gerando um desem-
penho de tempo real insatisfatório. A linguagem Java deixa a cargo do sistema operacional a
especificação das regras para a sincronização e escalonamento, podendo resultar em desempen-
hos diferentes ao utilizar sistemas operacionais distintos (SUN, 2006e). O sincronismo, que é
baseado em monitores e transferência de dados, é implementada através do compartilhamento
de memória (KLEBANOV et al., 2005).
O núcleo do modelo de threads de Java é derivado dos conceitos tradicionais de multithread-
ing. A literatura sobre threads e sincronização de threads é na maior parte sobre a compreensão
do sistema operacional ou dos conceitos de baixo nível de multithreading (HILDERINK et al.,
1997). A liberdade dada pelas APIs para a utilização de threads aumentam os perigos de cor-
rida de hazards, deadlock, livelock, starvation. O programador deve ter muito cuidado, e deve
aplicar um grande conjunto de regras, normas ou padrões de projeto.
Analisar um programa multithread e eliminar os erros de estados das threads pode ser ex-
tremamente difícil utilizando um API. Métodos teóricos foram desenvolvidos para analisar o
comportamento de aplicações multithread e eliminar os perigos gerados por ela, porém são
pouco eficientes. Para tentar evitar os problemas gerados pela programação multithread de
12
3 Communicating Sequencial Process(CSP)
Communicating Sequential Processes (CSP) é uma teoria matemática para descrição de
computação concorrente e distribuída, que pode ser usada para desenvolver aplicações multi-
thread que são garantidamente livres dos problemas comuns de concorrência, e o mais impor-
tante, pode ser provado através de regras matemáticas (HOARE, 1983). O seu comportamento é
baseado em teoria matemática, a qual é descrita por um conjunto de leis algébricas.
Um programa CSP é um conjunto de processos que se interconectam através de canais, a
única forma de interação entre processos, utilizando métodos de escrita e leitura para a troca
de informações com o meio. A comunicação ocorre de forma síncrona, com o fluxo direcional
da informação. Um processo que executa uma primitiva de comunicação (escrita ou leitura)
é bloqueado até que o processo com o qual está tentando se comunicar executa a primitiva
correspondente. Comandos de escrita e leitura são ditos correspondentes.
A teoria matemática fornece uma definição robusta do conceito de processo, e dos oper-
adores nos quais os processos são construídos. Essas definições são a base para as leis algébri-
cas, para a implementação e a prova das regras que garantem a exatidão das aplicações.
A teoria de CSP especifica inteiramente o comportamento da sincronização e escalona-
mento das threads em alto nível de abstração, que é baseado em processos, em composições e
primitivas de sincronização.
Existem dois pacotes principais que implementam o modelo CSP para a linguagem Java, o
Communicating Sequential Processes for Java (JCSP)(WELCH, 2006) desenvolvido pelo Profes-
13
sor Peter Welch e por Paul Austin na Universidade de Kent, da Inglaterra, e o Communicating
Threads for Java (CTJ)(HILDERINK, 2006) desenvolvido por Gerald Hilderinks na Universidade
de Twente, dos Países Baixos.
Ambos são baseados nas primitivas de CSP, mas possuem diferenças em seus projetos e
objetivos. JCSP foi criado como uma alternativa mais lógica a API de concorrência de Java,
combinando as capacidades de CSP com a aceitação da linguagem Java, utilizado na progra-
mação concorrente em geral. Já CTJ é um pacote criado para o desenvolvimento de aplicações
embarcadas e de tempo real. Ambos têm como objetivo tornar a programação concorrente mais
segura, porém sem aumentar a sua dificuldade. O pacote CTJ controla diretamente as threads,
pois possui seu próprio kernel que especifica o comportamento do sincronismo e escalonamento
das threads, tornando-se independente de plataforma e de comportamento previsível (caracterís-
tica de programação tempo real). Já JCSP usa as construções de concorrência da linguagem,
como os métodos wait, notify e synchronized .
Uma vantagem importante é que o programador pode aplicar um conjunto de regras para
eliminar corrida de hazards, deadlock, livelock, starvation, durante o projeto.
O pacote escolhido foi o CTJ, pois especifica o comportamento do escalonamento e do
sincronismo, assumindo o controle das threads, não mais deixando para o sistema operacional,
e dando uma maior previsibilidade ao programa. Compreender o conceito não requer muito
conhecimento sobre a teoria de CSP. O pacote de CTJ constrói uma ponte entre a teoria e a
prática de CSP.
14
4 Diferenças entre threads de Java eprocessos CSP
Os termos thread e processo são muito próximos. Um processo encapsula seus dados e
métodos, da mesma forma que os objetos, e encapsulam também uma ou mais threads de con-
trole. Ao atribuir uma thread de controle a um objeto passivo será criado um objeto ativo,
tornando-o um processo.
Um processo CSP é um objeto ativo cujas instruções são executadas por uma thread de
controle que está encapsulada no processo. Canais são objetos passivos e essenciais entre pro-
cessos, pois são utilizados para o envio e recebiemto de mensagens. Variáveis devem ser man-
tidas dentro do espaço de memória do processo, e não precisam ser sincronizadas, pois cada
processo possui seu endereçamento. Apenas dados de leitura podem ser compartilhados entre
os processos.
Processos podem iniciar processos, mas não podem controlar diretamente o processo filho.
Além de seguro, a depuração dos processos é menos complexa que seguir a execução de threads
baseadas no modelo utilizado por Java.
O comportamento das threads de Java é pouco especifico e fortemente dependente dos
padrões adotados pelo sistema operacional (SUN, 2006e). Então, o comportamento das threads
em diferentes máquinas virtuais Java (JVM) pode variar, enquanto o comportamento dos pro-
cessos deverá ser semelhante em qualquer máquina virtual Java, JVM.
A principal diferença entre threads e processos é que os processos são tipicamente indepen-
dentes (espaço de endereçamento diferente), um processo não sabe da existência de qualquer
15
outro processo, estes interagindo apenas com os mecanismos de comunicação, os canais, já as
threads tipicamente dividem o mesmo espaço de endereçamento e podem compartilhar objetos
em memória e recursos do sistema diretamente.
16
5 Primitivas de sincronização
Em Java, um objeto pode ter mais de uma thread. Threads que podem operar simultane-
amente dados compartilhados devem ser sincronizadas para prevenir corrida de hazard, que
pode resultar em dados corrompidos ou estados inválidos (trava do sistema). O usuário deve
controlar cada thread utilizando vários métodos, os quais devem ser usados de forma apropri-
ada. Esse conjunto de métodos (ver as classes java.lang.Thread (SUN, 2006c) e java.lang.Object
(SUN, 2006a) fornecem um conceito básico e flexível de multithreads. A clausula synchronized
(que nada mais é que a construção de um monitor) protege a região crítica (trecho de código
que tem acesso a dados compartilhados e pode ser utilizado por mais de uma thread, permitindo
que apenas uma thread tenha acesso a essa região por vez). Os métodos wait()/notify() fazem
o enfileiramento condicional das threads que necessitam utilizar as regiões críticas em comum.
A construção de um monitor de sincronização envolve manter sobre observação mais que um
método. Não é fácil determinar quais métodos ou trechos de código devem ser sincronizadas.
Determinados padrões de projeto podem resolver esse problema (HILDERINK et al., 1997), mas
eles podem tornar o programa mais complexo que o necessário. Além disso, inserir sincroniza-
ção entre dois métodos corretamente pode ser difícil e suscetível a erros.
Canais são objetos especiais que gerenciam a comunicação entre processos, de uma forma
pré-definida. Canais são objetos passivos situados entre os processos e que controlam a sin-
cronização, o escalonamento, e transferência de mensagens entre processos. Comunicação por
canais é considerada thread-safe (trecho de código que pode ser executado com segurança em
um ambiente multithread), mais confiável e rápido. Além disso, o programador estará livre de
implementar a sincronização e o escalonamento. Canais CSP são inicializados sem buffer e são
17
sincronizados de acordo com o princípio do rendezvous (o processo escritor espera até que o
processo leitor esteja pronto ou o processo leitor espera até que o escritor esteja pronto). Os
canais CTJ permitem zero ou mais buffers, de acordo com as suas necessidades. A simplici-
dade de se utilizar canais ao invés de monitores é uma importante motivação para a utilização
do conceito de CSP.
18
6 Estados de transição
Para cada processador haverá apenas uma thread em execução em um determinado mo-
mento. Um sistema multiprocessador com n processadores poderá ter n threads executando
simultaneamente. Entretanto um sistema uniprocessador pode executar múltiplas threads uti-
lizando um escalonador. Os estados das threads e processos não precisam ser iguais, embora
eles possam rodar no mesmo sistema. A seguir serão mostradas as diferenças entre estados de
transição de threads e processos.
6.1 Estados de transição da thread.
Uma thread pode estar em um dos seguinte estados (SUN, 2006d):
• new : a thread foi instanciada, porém ainda não está em execução;
• runnable: thread já em execução, de posse do processador;
• blocked: a thread está bloqueada, esperando ser liberada pelo monitor (método syn-
cronyzed);
• waiting: a thread está esperando indefinidamente que outra thread execute uma ação, um
signal;
• timed waiting: a thread está esperando que outra thread execute uma ação até um deter-
minado tempo;
• terminated : a thread terá terminado sua execução.
19
O diagrama de transição de estados mostrado na figura 1 exemplifica o modelo de transição das
threads.
Figura 1: Diagrama de transição de estados das threads.
6.2 Estados de transição de um processo.
Um processo pode estar em um dos seguintes estados (HILDERINK et al., 1997):
• instantiated: o processo pode ter sido criado ou terminou com sucesso, nenhuma thread
está atribuída ao processo;
• running: a thread de controle foi alocada a um processo e então o processo foi ativado;
• preempted: o processo foi preemptado por um de maior prioridade, o processo está pronto
para execução, mas não está executando;
• waiting: a thread de controle está inativa ou bloqueada e está esperando para ser notificada
(voltar ao processador);
• garbage: o processo terminou e não será atribuído a mais nenhuma thread, o garbage
collector de Java excluirá o objeto.
O diagrama de transição de estados mostrado na figura 2 na próxima página ilustra as transições
de estados de um processo:
20
Figura 2: Diagrama de transição de estados dos processos (HILDERINK et al., 1997).
O diagrama de transição de estados de processo distingue entre escalonadores preemptivos e
não preemptivos. Um processo que é preemptado por um processo de maior prioridade deve ser
temporariamente interrompido e ir para o estado preempted. Um processo preemptado passará
para o estado running caso não exista outro processo com prioridade maior esperando para
executar no estado preempted.
21
7 Processos CTJ
Processos que executam paralelamente não ”vêem” um ao outro. Cada processo enxerga
apenas seus canais, e isso é tudo que eles precisam. Em Java, um processo pai cria um processo
filho e inicia a execução do mesmo com o método run(), não havendo mais nenhuma interação
direta entre os dois. A interface de processo CTJ (HILDERINK, 2006h) é especificada por uma
interface de processo passiva, especificando o método run(), e uma interface de processo ativa,
especificando o conjunto de canais entrada/saída que serão usados pelo processo.
A interface de processo pode ser derivada de modelos data-flow (a comunicação é feita
pela emissão das mensagens aos receptores), que são grafos rotulados e orientados, composto
pelos processos (nodos) e pelos canais (arestas). Processos são conectados por arestas e elas
especificam a direção das mensagens. A figura 3 mostra um grafo de uma interface de um
processo ativo com um canal de entrada e outro de saída.
Figura 3: Interface de processo (HILDERINK et al., 1997).
22
8 Canal CTJ
A interface de canais do pacote CTJ (HILDERINK, 2006b) é simples, contendo uma interface
para canais de entrada, que especifica o método read(), e uma interface para canais de saída
que especifica o método write(). Processos comunicam-se com outros processos utilizando
leitura ou escrita nos canais compartilhados invocando o métodos read() e write(). Canais CTJ
permitem também múltiplas escritas e leituras.
Para evitar a perca de dados, os canais de comunicação são sincronizados. Isso significa
se o emissor envia mensagem antes do receptor estar pronto para receber, o emissor será blo-
queado. Da mesma forma, se o receptor tentar receber antes que o emissor tenha enviado
uma mensagem, o receptor será bloqueado. Quando ambos estão prontos os dados podem ser
transmitidos. Os Canais do pacote CTJ enviam, por default, mensagem por valor, evitando o
compartilhamento de dados, embora também seja possível enviar mensagem por referencia. O
envio de mensagens por valor faz com que tanto canais de comunicação interna (utilizando o
mesmo disco físico) quanto canais externos (discos físicos diferentes) tenham o mesmo trata-
mento. Os canais do JCSP (P.D.AUSTIN, 2006) enviam mensagens por referência, menos para a
primitiva int, sendo necessário um tratamento especial para a transmissão de dados em canais
externos, pois não compartilham o mesmo espaço de endereçamento.
Comunicação via canais, fornece um estrutura independente de hardware e uma estrutura
dependente de hardware. Ambas as estruturas são conectadas por uma interface de canal muito
simples, figura 4 na próxima página.
23
Figura 4: Interface de canal CTJ (HILDERINK et al., 1997).
8.1 Independente de hardware:
A comunicação via canais fornece uma plataforma independente de estrutura, onde os pro-
cessos podem ser alocados no mesmo sistema ou distribuídos em outros sistemas. Os processos
nunca acessam diretamente o hardware, eles podem apenas se comunicar com seu ambiente por
meio dos canais. Como resultado, os processos não sabem quais processos estão do outro lado
do canal e não sabem que hardware está entre os dois.
8.2 Dependente de hardware:
Canais podem estabelecer um link entre dois ou mais sistemas via algum hardware. Ob-
jetos especiais chamados linkdrivers podem ser inseridos no canal. Os linkdrivers controlam
o hardware e são as únicas partes da aplicação que possuem dependência de hardware. A es-
trutura do linkdriver é definida como abstrata, e pode ser estendida conforme a necessidade,
sem influenciar o projeto ou as estruturas independentes de hardware. A estrutura do linkdriver
também fornece tratamento de interrupções.
Todos os processos que não utilizam linkdriver são totalmente independentes de hardware.
Os processos que utilizam linkdrivers podem ser mais ou menos dependentes do hardware, pois
o linkdriver representa diretamente o hardware. A utilização de linkdriver facilita a portabil-
24
idade das aplicações, pois caso haja a necessidade da utilização de outro hardware, as mod-
ificações no projeto serão mínimas, apenas substituindo os atuais linkdrivers por uma versão
estendida para o novo hardware.
Os linkdrivers fazem a comunicação com a arquitetura de hardware, recursos do sistema
e fornecem um comportamento opcional, determinando a forma de comunicação, seja interna
(através da memória utilizando rendezvous ou buffers) ou externa (via periféricos como RS232,
PCI ou TCP/IP). Podem ser inseridos em um canal fornecendo os protocolos de comunicação
para a transferência de dados através do hardware. A combinação entre canais e linkdrivers
é poderosa, pois fornece uma estrutura para comunicação tanto interna como externa. Como
resultado os processos se tornam altamente independentes do meio físico.
Um canal pode facilmente estender um linkdriver, como mostra a algoritmo 1.
Algorithm 1 Canal estendendo um linkdriver.
25
9 Utilizando CTJ
Essa seção descreve como criar processos e composição de processos em Java, utilizando o
pacote CTJ.
9.1 Criando processos em Java
Um processo é definido pela interface csp.lang.Process (HILDERINK, 2006h). Uma classe
de processo deve implementar a interface csp.lang.Process, que é muito semelhante a interface
java.lang.Runnable (SUN, 2006b). O método run() implementa o corpo runnable do processo,
que será invocado por outro processo e executará uma tarefa seqüencial.
Algorithm 2 Exemplo de uma classe que implementa a interface de Processo.
O construtor do processo deve especificar os canais de entrada e saída e os parâmetros para
inicialização do processo. Não mais que um processo pode invocar o método run() ao mesmo
tempo. O método run() só poderá começar sua execução se os recursos necessários estiverem
disponíveis, para um funcionamento confiável. Na instanciação de um processo, seu construtor
deve inicializar todos os recursos, como os canais de entrada e saída e os parâmetros, antes que
27
10 Composição de processos
Basicamente, processos são executados quando o método run() é invocado. O processo que
o chamou espera até o método run() retornar com sucesso.
A teoria de CSP descreve composições comuns nas quais os processos são executados, ou
seja, processos podem executar em seqüência ou em paralelo. Nessa seção serão apresentadas
as seguintes construções de composições: Seqüencial (10.1), Paralelo (10.2), Pararelo com
prioridade (10.3), Alternativo (10.4) e Alternativo com prioridade (10.5).
10.1 Construtor de Composição Seqüencial - Sequencial
O construtor de composições seqüencial executa apenas um processo por vez. O processo
de composição seqüencial terminará quando todos os processos internos forem finalizados. O
construtor de composições seqüencial é criado pela classe Sequential (HILDERINK, 2006g). O
objeto seqüencial é um processo.
Algorithm 4 Construtor de composição seqüencial.
No algoritmo 4 o argumento processos é um Array de processos. O construtor inicia quando
invocado seu método run().
O algoritmo 5 na página seguinte mostra uma composição seqüencial de três processos.
28
Algorithm 5 Exemplo de uma composição seqüencial.
O Processo2 será executado após o Processo1 ter finalizado sua execução com sucesso, e o
Processo3 após a execução bem sucedida do Processo2. O processo de composição sequencial
é bem sucedido quando todos os processos foram executados com sucesso.
Novos processos podem ser adicionados no fim lista, algoritmo 6:
Algorithm 6 Adição de um novo processo a composição seqüencial.
ou ainda, algoritmo 7:
Algorithm 7 Adição de vários novos processos a composição seqüencial.
E novos processos podem ser inseridos em uma determinada posição na lista como no
algoritmo 8:
Algorithm 8 Inserção de processo em determinada posição na composição seqüencial .
Podem ser removidos da lista de processos 9:
Algorithm 9 Remoção de processos da composição seqüencial .
Os métodos mencionados acima só podem ser executados pela instância que criou o pro-
cesso de composição, quando este não estiver em execução. Essas restrições asseguram a con-
fiabilidade e segurança para o construtor.
29
10.2 Construtor de Composição Paralelo - Parallel
O construtor de composições paralelas executa processos em paralelo. O construtor termina
quando todos os processos forem finalizados com sucesso. O construtor é criado pela classe
Parallel (HILDERINK, 2006d), sendo esse objeto um processo.
Algorithm 10 Construtor de composição Paralelo.
No algoritmo 10 o argumento processos é um Array de processos. O construtor é inicial-
izado quando o método run() é invocado.
O exemplo 11 mostra uma composição em paralelo de três processos.
Algorithm 11 Exemplo de uma composição Paralela.
Os processos Processo1, Processo2 e Processo3 serão executados em paralelo. Cada um
terá uma thread de controle interno com a mesma prioridade de execução. O processo paralelo
será finalizado com sucesso, se todos seus processos internos forem bem sucedidos.
Novos processos podem ser inseridos a lista, algoritmo 12:
Algorithm 12 Adição de um novo processo a composição Paralela.
ou ainda como no algoritmo 13:
Algorithm 13 Adição de vários novos processos a composição Paralela.
Da mesma forma, processos podem ser removidos:
30
Algorithm 14 Remoção de processos da composição seqüencial.
10.3 Construtor de Composição Paralelo baseado em Priori-dade - PriParallel
O construtor de prioridades priparalelo (HILDERINK, 2006f) estende o construtor de com-
posição paralela, porém agora com prioridades. A cada processo do construtor priparalelo será
dada uma prioridade, enquanto que os processos do construtor de composição paralela rece-
biam a mesma prioridade. O primeiro processo da lista receberá a maior prioridade, e o último
receberá a menor prioridade da lista de processos. O objeto priparalelo é um processo.
Atualmente, o número máximo de prioridade por objeto priparalelo é 8, onde 7 são para os
processos e um é reservado para as tasks idle, skip e garbage collector (ainda não implemen-
tado). Processos priparalelos podem ser inicializados aninhados ( 13 na página precedente),
assim, podendo aumentar o número de processos com prioridades.
Os processos são executados por prioridade, tais prioridades são definidas pela ordem de
inserção na lista de processos, não sendo possível fazer a edição das mesmas para os processos
já adicionados.
A classe PriParallel cria um processo paralelo baseado em prioridade.
Algorithm 15 Construtor de composição Paralelo com prioridade.
O argumento processos é uma Array de Process. O construtor é iniciado pela chamada do
método run().
O exemplo 16 mostra uma composição paralela de três processos com prioridade:
31
Algorithm 16 Exemplo de uma composição Paralelo com prioridade.
Os processos Processo1, Processo2 e Processo3 serão executados em paralelo com priori-
dades sucessivas. O Processo1 (de índice 0) tem a maior prioridade. Todos os processos da lista
que possuem índice 6 ou maior, compartilham a menor prioridade, 6. O processo priParalelo
termina com sucesso quando todos os processos internos são executados com sucesso. Para au-
mentar o número máximo de prioridades é possível criar novos processos PriParellel aninhados
( 10.6 na página 36) ao processo já criado. O exemplo 17 mostra um construtor priparalelo com
28 níveis de prioridade.
32
Algorithm 17 Exemplo de uma construção aninhada da composição Paralela com prioridade,
aumentando os níveis de prioridade.
Novos processos podem ser adicionados em tempo de execução:
Algorithm 18 Adição de um novo processo a composição Paralela com prioridade.
Ou ainda:
Algorithm 19 Adição de vários novos processos a composição Paralela com prioridade.
Processos podem ser inseridos em determinada posição da lista de processos (recebendo a
prioridade de tal índice):
33
Algorithm 20 Inserção de processo em determinada posição na composição Paralela com pri-
oridade.
Processos podem ser removidos:
Algorithm 21 Remoção de processos da composição Paralela com prioridade.
A ordem das prioridades será atualizada com a inserção ou remoção de um processo.
10.4 Construtor de Composição Alternativo - Alternative
O construtor de composição alternativa é composto por guardas, sendo cada guarda um pro-
cesso. Assim que um guarda fica pronto, ele é executado pelo processo principal. O construtor
da composição alternativa é definida pela classe Alternative (HILDERINK, 2006a).
Algorithm 22 Construtor de composição Alternativo.
O argumento guardas é um Array de objetos guardas (ver capítulo 11 na página 38). Um
objeto guarda é uma instância da classe Guard (HILDERINK, 2006c). O processo construtor é
iniciado pelo método run().
O exemplo 23 mostra uma composição alternativa para três processos.
Algorithm 23 Exemplo de uma composição Alternativa.
34
O processo alternativo espera pelo menos um dos guardas estar pronto, mas termina quando
um dos processos for selecionado e finalizado com sucesso. O guarda que possui o processo de
índice X, será selecionado quando o canal de mesmo índice estiver pronto. Sendo este canal o
canal de entrada do processo índice X. Se mais que um guarda estiver pronto para executar eles
serão selecionados se forma randômica.
Novos guardas podem ser inseridos em tempo de execução:
Algorithm 24 Inserção de um novo Guard na composição Alternativa.
ou ainda:
Algorithm 25 Inserção de vários Guard na composição Alternativa.
E podem ser removidos também:
Algorithm 26 Remoção de Guard da composição Alternativa.
10.5 Construtor de Composição Alternativo baseado em Pri-oridade - PriAlternative
A classe PriAlternative (HILDERINK, 2006e) cria um construtor de composição alternativo
baseado em prioridade. Ela estende a classe Alternative e substitui o mecanismo de escolha
randômica por um baseado em prioridade. O objeto priAlternativo é um processo. O construtor
prialternativo é semelhante do alternativo, algoritmo .
Algorithm 27 Declaração de um objeto da classe PriAlternative.
35
O argumento guardas é um Array de objetos Guarda. Um objeto guarda é uma instância
da classe Guard (HILDERINK, 2006c). O construtor PriAlternativo é inicializado pelo método
run().
O algoritmo 28 mostra uma composição PriAlternative para três processos
Algorithm 28 Exemplo de uma composição Alternativa com prioridade,
O processo priAlternativo espera até pelo menos um guarda estar pronto, mas termina com
sucesso quando um dos processos internos é selecionado e finalizado com sucesso. O guarda
com o processo de índice X será selecionado quando o canal de mesmo índice estiver pronto.
Sendo este o canal de entrada do processo X. Se mais de um guarda estiver pronto, o guarda
de maior prioridade (menor índice) será selecionado. O processo pertencente ao guarda sele-
cionado será executado.
Um novo guarda pode ser adicionado em tempo de execução, algoritmo 29.
Algorithm 29 Adição de um novo guarda a composição PriAlternative.
Ou vários guardas podem ser inseridos na composição, algoritmo 30.
Algorithm 30 Adição de vários guardas a composição PriAlternative.
Um guarda pode ser inserido por índice na composição PriAlternativa, algoritmo 30.
36
Algorithm 31 Adição de vários guardas a composição PriAlternative.
Sendo a prioridade do guarda inserido igual ao seu índice, e as prioridades dos guardas com
índice maior serão incrementadas.
Sendo possível a remoção de guardas, algoritmo 32.
Algorithm 32 Adição de vários guardas a composição PriAlternative.
10.6 Construtor de Composição Aninhados
Os processos de composição Seqüencial ( 10.1 na página 27), Paralela ( 10.2 na página 29),
Paralela com prioridade ( 10.3 na página 30), Alternativa ((10.4)) e Alternativa com prioridade
((10.5)) aceitam processos de composição aninhados. Duas composições seqüenciais podem
executar em paralelo, como no algoritmo (33).
Algorithm 33 Exemplo de composição aninhada, composição paralela com duas composições
seqüenciais aninhadas.
Ou uma composição alternativa que optará entre a execução da composição paralela ou
seqüencial, como no algoritmo (10.6).
37
Exemplo de composição aninhada, composição alternativa com duas composições aninhadas,
uma paralela e outa seqüencial.
38
11 Guardas Condicionais eIncondicionais
O objeto guarda é uma instância da classe Guard (HILDERINK, 2006c), que controla um
processo, que estará pronto quando o construtor receber a primeira ocorrência de comunicação
no canal de entrada desse processo. O processo é controlado por entradas, ou seja, apenas os
canais de entrada podem ser monitorados pelos processos. O controle dos canais de saída não
foi implementada por que isso resultaria em uma penalidade na performance do canal.
Um objeto guarda pode ser declarado como no algotimo 34.
Algorithm 34 Criando um objeto da classe Guard.
O guarda se torna true quando o argumento canal (interface de entrada) está pronto e tem
dados disponíveis para leitura. O guarda em si não é um processo, mas um objeto passivo
que controla os processos. O objeto alternativo (ver 10.3 na página 30) verifica se todos os
guardas estão disponíveis e espera pelo menos até que um canal fique pronto, então o processo
que pertence ao guarda ativado pode ser selecionado e executado. Um guarda habilitado que
sempre é executado no construtor alternativo é chamado de incondicional. Um guarda também
pode ser condicional, isto é, será habilitado (participará do construtor alternativo) se alguma
condição for verdadeira, ou será desabilitado (não participa do construtor) e o processo não é
executado.
Algorithm 35 Execução condicional de um Guard.
39
Se a condição for verdadeira o guarda verificará o canal, porém, se for falsa, o guarda não
executará e o processo não será selecionado. Se o processo for selecionado, deverá ler os dados
do canal.
Um guarda pode ser declarado com uma variável condicional, algoritmo 36.
Algorithm 36 Declaração de um Guarda condicional.
Ou sem a variável, algoritmo 37.
Algorithm 37 Declaração de um Guarda incondicional.
A condição de execução do guarda pode ser modificada pelo método setEnabled().
40
12 Validação do programas CSP
O seu comportamento da linguagem CSP é baseado em teoria matemática, a qual é descrita
por um conjunto de leis algébricas. Sendo assim, o comportamento de uma aplicação CSP
pode ser validado utilizando-se regras matemáticas, garantindo a ausência problemas comuns a
programação concorrênte. Existem várias ferramentas que fazem a validação de aplicações CSP,
uma delas é o Deadlock Checker (MARTIN; JASSIM, 2007), que verifica a existência de deadlock
e livelock a partir da construção de um sistema de transições de toda a rede de processos,
verificando cada estado global em busca de deadlock (MARTIN; JASSIM, 1997).
Já os pacotes que implementam o modelo CSP em Java, apenas o JCSP da suporte a val-
idação de código, através de um sistema de prova, que estende os calculos da tecnologia Java
Card (SUN, 2007), que corresponde a linguagem de programação Java sem threads e é usada
para programação de smartcards. Uma parte importante do sistema de prova é o calculo para
da lógica do programa, que é feito através do JavaCard Dynamic Logic (JavaCardDL) que foi
desenvolvido no projeto KeY. A ferramenta KeY é um sistema para verificação de programas
Java Card, podendo também verificar programas Java sem threads (KLEBANOV et al., 2005).
A figura 5 na página seguinte mostra a arquitetura de verificação do sistema, a qual consiste
em quatro componentes. A verificação do código possui quatro passos:
1. O primeiro passo executa instruções JavaCard até encontrar uma chamada para alguma
biblioteca JCSP. Isso é executado pelos padrões de calculo do programa KeY. Devido à
comunicação entre processos ser através de canais, de forma explicita, não existe interfer-
ência no código seqüencial de um processo. Do ponto de vista CSP, um código seqüencial
41
pode ser visto como um processo que produz apenas eventos internos.
2. A segunda parte, executando em paralelo com a primeira, substitui as bibliotecas JCSP
utilizadas no programa por seus modelos em CSP.
3. O passo 3 reescreve o programa, transformando os processo em uma forma normal que
permite uma dedução mais simples dos passos de um processo.
4. No quarto passo, as asserções são avaliadas segundo os possíveis comportamentos dos
processos.
Figura 5: Arquitetura do calculo de verificação (KLEBANOV et al., 2005).
Com a forma normal dos processos montada ocorrem duas fases de validação deste código.
Na primeira parte a rede de processos (uma espécie grafo que possui os processos como no-
dos e os canais como arestas) é montada. O segundo passo envolve a execução desta rede
de processos, simulando a troca de mensagens entre os processos. A prova fornece uma boa
representação passo a passo da execução da rede, servindo para a depuração.
42
13 Comparação entre aplicaçõesutlizando CTJ e Java
Para verificar o custo da utilização do pacote CTJ em aplicações concorrentes, ao invés do
modelo de thread utilizado por Java, foi implementado um problema clássico de concorrência,
o Jantar dos Filósofos.
O Problema do Jantar dos Filósofos apresenta cinco (5) filósofos e cinco (5) garfos. A
idéia central deste problema é que dado os filósofos e os garfos em um jantar, cada filósofo
necessita de dois (2) garfos para comer. Os filósofos podem estar em um destes dois (2) estados:
esperando ou comendo. Se um filósofo está no estado esperando e quer passar para o estado
comendo, ele tenta pegar dois (2) garfos. Se conseguir pegar os dois garfos ele passa para
o estado comendo. Enquanto no estado esperando, o filósofo permanece tentando pegar os
garfos, ou seja, fica esperando a liberação dos garfos.
Foram desenvolvidas duas implentações deste problema, uma utilizando as threads de Java
(ver apêndice 14 na página 50, 14 na página 51 e 14 na página 52), e outra utilizando o pacote
CTJ (ver apêndice 14 na página 46, 14 na página 48 e 14 na página 49).
A rede de processos dessa aplicação está representada na figura .
43
Figura 6: Rede de processos do Jantar dos Filósofos.
Os códigos resultantes são semelhantes, embora a versão CTJ necessite de um pouco mais
de lógica para o tratamento das mensagens, tanto no envio quanto no recebimento.
A proteção das regiões criticas na implementação utilizando CTJ é mais simples, pois a
sincronização de CTJ é feita pelos canais de comunicação, não existindo a preocupação de
quais trechos de códigos será necessária a inserção da clausula synchronized, diferente de Java,
que uma das dificuldades é escolher quais métodos serão sincronizados.
A execução do jantar dos filósofos em Java no intervalo de um minuto, gerou uma média
de 2058400 acessos aos métodos que são utilizados para o acesso aos dados (são os métodos
pegaGarfo() e devolveGarfo() da classe MesaJava 14 na página 50), já na versão utilizando
CTJ, com o mesmo intervalo de tempo, ocorreu em media 6250 leituras no canal de entrada da
44
classe MesaCTJ, ou seja, o número de vezes que a classe MesaCTJ interagiu com um Filósofo.
Essa grande diferença entre o número de interações se deve em parte, ao controle das
threads, que CTJ assume completamente, ao invés de deixá-lo a cargo do sistema operacional
como Java, afetando seu desempenho. A troca de mensagens também é uma das responsáveis
pela queda no desempenho, pois a cada escrita em um canal, o objeto a que se deseja transmitir
é copiado, e a sua copia é enviada ao canal.
45
14 Conclusão
O pacote Communicating Threads for Java (CTJ) é uma implementação do modelo CSP,
resultando em construtores baseados em processos, composições e canais, de uso muito mais
simples e mais confiável que o modelo Java. O pacote CTJ fornece um pequeno conjunto de
padrões de projeto que é suficiente para programação concorrente em Java. Uma importante
vantagem do pacote CTJ é que o programador pode aplicar um conjunto de regras para eliminar
os race-hazards, deadlock, livelock, starvation, etc.
A sincronização e o escalonamento dos processos foi muito simplificado com a comuni-
cação entre canais e construtores compostos, tornando mais fácil a depuração e o acompan-
hamento dos estados do processo.
CTJ possui seu próprio kernel, que faz o escalonamento de suas thread de modo previsível
e claro, porém afetando seu desempenho.
O programador não precisar se preocupar com a teoria matemática de CSP, pois é um con-
ceito maduro, que possui ferramentas para verificação de erros.
Ao desenvolver aplicações utilizando CSP, a decisão de qual plataforma (plataforma mul-
tithread, vários processos rodando em um processador, vários processos sendo executados em
diferentes processadores) será utiliza pode ser tomada sem muito impacto ao código e ao projeto
da aplicação.
Usar CSP como base para o seu programa assegura a separação entre processos, pois a
interação entre os mesmo é feita somente através de canais, e não de métodos. Isso também
assegura o encapsulamento dos dados e da lógica de cada processo, sem a interação direta de
46
outro processo sobre estes.
Classe MesaCTJ
Algorithm 38 Parte 1 da classe Mesa, da aplicação Jantar dos filósofos implementada em CTJ.
48
Algorithm 40 Parte 3 da classe Mesa, da aplicação Jantar dos filósofos implementada em CTJ.
Classe FilosofoCTJ
49
Algorithm 41 Classe Filosofo, da aplicação Jantar dos filósofos implementada em CTJ.
Classe JantarDosFilosofosMain
50
Algorithm 42 Classe JantarDosFilosofos, da aplicação Jantar dos filósofos implementada em
CTJ.
Classe MesaJava
51
Algorithm 43 Classe Mesa, da aplicação Jantar dos filósofos implementada em Java.
Classe FilosofoJava
52
Algorithm 44 Classe Filosofo, da aplicação Jantar dos filósofos implementada em Java.
Classe MainJava
Algorithm 45 Classe Main, da aplicação Jantar dos filósofos implementada em Java.
53
Referências
HILDERINK, Gerald. Communicating Sequential Processes for JavaTM (JCSP). Universityof Twente: [s.n.], Setembro 2006. Disponível em: <http://www.ce.utwente.nl/javapp/>.
HILDERINK, Gerald; BROENINK, Jan; VERVOORT, Wiek; BAKKERS, Andre.Communicating Java Threads. In: BAKKERS, A. (Ed.). Parallel Programming and Java,Proceedings of WoTUG 20. University of Twente, Netherlands: IOS Press, Netherlands, 1997.v. 50, p. 48–76. Disponível em: <citeseer.ist.psu.edu/hilderink97communicating.html>.
HILDERINK, Gerald H. Class Alternative. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/Alternative.html>.
HILDERINK, Gerald H. Class Channel. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/Channel.html>.
HILDERINK, Gerald H. Class Guard. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/Guard.html>.
HILDERINK, Gerald H. Class Parallel. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/Parallel.html>.
HILDERINK, Gerald H. Class PriAlternative. Setembro 2006. Disponívelem: <http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/PriAlternative.html>.
HILDERINK, Gerald H. Class PriParallel. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/PriParallel.html>.
HILDERINK, Gerald H. Class Sequential. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/Sequential.html>.
HILDERINK, Gerald H. Interface Process. Setembro 2006. Disponível em:<http://www.cs.bgu.ac.il/ elhadad/advpro/ctj-distribution/ctj-doc/csp/lang/Process.html>.
HOARE, C. A. R. Communicating sequential processes. Commun. ACM, ACM Press, NewYork, NY, USA, v. 26, p. 100–106, 1983. ISSN 0001-0782.
KLEBANOV, Vladimir; RÜMMER, Philipp; SCHLAGER, Steffen; SCHMITT, Peter H.Verification of JCSP programs. In: BROENINK, J.F.; ROEBBERS, H.W.; SUNTER,J.P.E.; WELCH, P.H.; WOOD, D.C. (Ed.). Communicating Process Architectures2005. IOS Press, The Netherlands: IOS Press, 2005. (Concurrent Systems EngineeringSeries, v. 63), p. 203–218. ISBN 1-58603-561-4. ISSN 1383-7575. Disponível em:<http://www.cs.chalmers.se/ philipp/publications/jcsp2005.pdf>.
54
MARTIN, J. M. R.; JASSIM, S. A. A Tool for Proving Deadlock Freedom. In: BAKKERS,A. (Ed.). Parallel Programming and Java, Proceedings of WoTUG 20. University of Twente,Netherlands: IOS Press, Netherlands, 1997. v. 50, p. 1–16.
MARTIN, J. M. R.; JASSIM, S. A. The Deadlock Checker Tool. The University of Kent: [s.n.],Janeiro 2007. Disponível em: <http://wotug.kent.ac.uk/parallel/theory/formal/csp/Deadlock/>.
P.D.AUSTIN. Interface Channel. Setembro 2006. Disponível em:<http://www.cs.kent.ac.uk/projects/ofa/jcsp/jcsp1-0-rc7/jcsp-docs/jcsp/lang/Channel.html>.
SUN. Class Object. Setembro 2006. Disponível em:<http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html>.
SUN. Class Runnable. Setembro 2006. Disponível em:<http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Runnable.html>.
SUN. Class Thread. Setembro 2006. Disponível em:<http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html>.
SUN. Enum Thread.State. Setembro 2006. Disponível em:<http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.State.html>.
SUN. Threading. Dezembro 2006. Disponível em:<http://java.sun.com/docs/hotspot/threads/threads.html>.
SUN. Java Card Technology. Janeiro 2007. Disponível em:<http://java.sun.com/products/javacard/>.
WELCH, Peter. Communicating Sequential Processes for JavaTM (JCSP). UNI-VERSITY OF KENT At Canterbury: [s.n.], Setembro 2006. Disponível em:<http://www.cs.kent.ac.uk/projects/ofa/jcsp/>.