INE 5645 - Programação Paralela e Distribuída


Tarefas Teóricas e de Programação

Material Didático
Unidade 1:    Introdução 2011

Unidade 2-Parte 1:   Programação Concorrente

Material de Apoio: Cap. 15-Deitel: Java Como Programar, Ed.4
           Ciclo de Vida de uma Thread, Métodos executados por threads.
           Prioridades de Threads
          

Analisar o exemplo, sobre a estruturação de threads, executando adormecimento e despertar de threads, usando herança direta da classe Thread. Os arquivos abaixo contém texto e código explicativo. 
Tarefa Inicial de Programação - Estruturação de Threads 15.4 Ed 4                          

A classe Thread serve para criar novas classes que suportam multithreading. Sobrescrevemos o método run() para programar as tarefas que serão realizadas concorrentemente.

Porém, se precisamos suportar multithreading em uma classe que já é herdada (derivada) de uma classe diferente da classe Thread, a estruturação de threads é feita implementando-se a interface Runnable nessa classe, porque Java não permite que se herde mais de uma classe ao mesmo tempo. A própria classe Thread implementa Runnable (interface no pacote (java.lang), como no exemplo:

public class Thread extends Object implements Runnable

Analisar um exemplo, sobre a estruturação de threads, executando o adormecimento e despertar de threads, usando a interface Runnable.
Tarefa Inicial de Programação - Estruturação de Threads com Runnable. 23.4 Ed 6

Criando um pool de número fixado de threads:
Um pool de threads é uma coleção de threads disponíveis para realizar tarefas.

Para um grande número de threads, um pools de threads, geralmente provêem:

  • Um programa com muitas threads pode sofrer em desempenho.
  • Melhor performance quando se executam um grande número de threads.

Para usar pools de threads, instancie uma implementação da interface ExecutorService e entregue tarefas para execução.

Essa implementação da interface ExecutorService permite que você estabeleça entre outras coisas:

  • O número básico e máximo do pool (número de threads).
  • Como criar e terminar threads.
  • Como tratar tarefas rejeitadas, isto é, sem poderem ser executadas porque todas as threads no pool estão em uso. Pode-se usar um número fixo de threads e com menos threads do que tarefas.

O método newFixedThreadPool(int) da classe Executors, cria um pool com número fixo de threads e fila ilimitada de tarefas.

No exemplo 23-5, threads são utilizadas por um objeto threadExecutor da interface ExecutorService. Desta forma os Runnables que implementam as threads, são executados.

No caso de haver um número fixo de threads, com menos threads do que tarefas, se o método Execute for chamado e todas as threads em ExecutorService (interface para gerenciar threads) estiverem em uso, a tarefa (implementada em Runnable) será colocada numa fila e atribuído à primeira thread que completar sua tarefa.

Interfaces e Classes usadas:
interface Runnable,
interface Executor,
interface ExecutorService, para gerenciar threads em um pool de threads.
classe Executors (com s no final é uma classe).

Unidade 2-Parte 2:  Concorrência em Java

Para se aprofundar, você pode ver os seguintes links:
Pool de Threads
Java Threading: the Executor Framework Um melhor mecanismo para executar threads em Java.

-------------------------------------------------------------------------------------------
 

Unidade 3:  Controle de Concorrência

Sincronização de Threads 
                   
Inanição = Starvation

Tarefa Inicial de Programação 3.1 - Produtor-Consumidor com Monitor.

Livro "Java Como Programar", Edição 6, Deitel & Deitel - Cap. 23 - Multithreading
Download de códigos do livro Deitel, Java: Como Programar Ed. 6: 

http://www.inf.ufsc.br/~bosco/downloads/Java 6 CD/ch23/fig23_6_10/  

Você deve procurar os arquivos fig23 6-10, onde 23.6 (Interface Buffer), 23.7 (class Producer) e 23.8 (class Consumer), que são os exemplos da seção 23.6 (Relacionamento entre produtor e consumidor sem sincronização).

Analise primeiro, os códigos em 23.9 e 23.10, que tratam da implementação de um buffer não-sincronizado UnsynchronizedBuffer e do aplicativo de teste SharedBufferTest.

Lembre-se que a corretude do programa é que a thread Producer execute primeiro e que cada valor produzido por Producer seja consumido exatamente uma vez pelo Consumer.

No código 23.10, a implementação SharedBufferTest, do aplicativo produtor-consumidor mostra duas threads que manipulam o buffer não-sincronizado UnsynchronizedBuffer.

Veja o resultado de sua execução !!!

O que você nota ???
-------------------------------------------------------------------------------------------

Analise, agora,  o relacionamento entre produtor e consumidor com sincronização, veja agora a seção 23.12 sobre Monitores em Java.

Novamente  você deve aproveitar os códigos 23.6 do Buffer e as classes Producer (em 23.7) e Consumer (em 23.8) do exemplo da seção 23.6.

http://www.inf.ufsc.br/~bosco/downloads/Java 6 CD/ch23/fig23_19_20/ 

Veja, agora, os códigos em fig23 19-20.


O código que realiza a sincronização é colocado nos métodos set e get da classe SynchronizedBuffer (Fig. 23.19), que implementa a interface Buffer. A explicação do exemplo é importante para o aprendizado sobre monitores. O código 23.20 SharedBufferTest2  mostra um aplicativo produto-consumidor que utiliza um buffer sincronizado.

Veja o resultado de sua execução.

O que você nota, agora ???

Explicação do Problema dos Leitores e Escritores

Pseudo-Código:  Leitores e Escritores (para entendimento do problema)

Laboratório 1: Threads e Monitores

Para realizar a tarefa do Laboratório 1, convém ver os slides em:
    Interfaces e Classes para Gerenciamento e Escalonamento de Threads

    Exemplos para Escalonamento de Threads em Diferentes Formas

--------------------------------------------------------------------------------------------

Tarefa Inicial de Programação 3.2 - Produtor-Consumidor com Locks
. Utilizar o mesmo exemplo de código da tarefa anterior sobre monitor e realizar sincronização usando locks.

Locks Explícitos e Variáveis de Condição

Relacionamento Produtor-Consumidor com sincronização usando Locks (Exemplo Deitel 6Ed. Cap. 23, Seção 23.7, pag. 800-805).

Sobre código reentrante:

Reentrância – É comum, em sistemas multiprogramáveis, vários usuários executarem os mesmos utilitários do SO simultaneamente, como, por exemplo, um editor de textos. Se cada usuário que utilizasse o editor trouxesse o código do utilitário para a memória, haveria diversas cópias de um mesmo programa na memória principal, o que ocasionaria um desperdício de espaço.

Reentrância é a capacidade de um código de programa (código reentrante) poder ser compartilhado por diversos usuários, exigindo que apenas uma cópia do programa esteja na memória. Uma característica da reentrância é que o código não pode ser modificado por nenhum usuário no momento em que está sendo executado.

A reentrância permite que cada usuário possa estar em um ponto diferente do código reentrante, manipulando dados próprios, exclusivos de cada usuário.

Os utilitários do sistema, como editores de texto, compiladores e linkers (linker editors), são exemplos de código reentrante, que proporcionam grande economia de espaço em memória e aumento na performance do sistema. Em alguns sistemas existe a possibilidade de utilizar o conceito de reentrância para aplicações de usuários.

(Reentrância)  Um link interessante:
http://www2.eletronica.org/artigos/eletronica-digital/reentrancia

Laboratório 2: Locks 

Explicando Locks com o método trylock()

Exemplo de Implementação Lab 2 (Locks)
Main.java
Estante.java
Professor.java
Estudante.java
Livro.java
BufferMensagem.java

Unidade 3:  Controle de Concorrência com Locks  (slides com aplicação à transações)


Tarefa Prática (Opcional):  Implementar a concorrência, usando locks, entre as duas transações T e U exemplificadas no slide 83. Você pode considerar a implementação de uma Interface Coordinator de transações, como no slide 37, a Classe Lock e a Classe LockManager, como mostradas ao final dos slides.

-------------------------------------------------------------------------------------------

Tarefa Inicial de Programação 3.3 - Produtor-Consumidor com Semáforo.

Exemplos de Produtor-Consumidor com Semáforo:

Montar o segundo exemplo do problema do Produtor-Consumidor com Sincronização, buffer limitado, da seção 23.12. Mas, usando semáforos (pacote (java.util.concurrent. Semaphore) Você deve procurar, no link acima, os arquivos das figuras 23.6 (Interface Buffer), 23.7 (Classe Producer) e 23.8 (Classe Consumer), do exemplo da seção 23.6. O código que realiza a sincronização é colocado nos métodos set e get da classe SynchronizedBuffer (Fig. 23.19), que implementa a interface Buffer.

O trabalho poderá ser feito em dupla, mas a dupla deverá estar presente para mostrar o programa funcionando. Postar a tarefa no Moodle até a data programada, a aula de terça-feira (16/03). Para quem é iniciante, um exemplo de programa a ser modificado, está no Cap. 23 do livro referido acima, nas seções . A explicação do exemplo é importante para o aprendizado sobre semáforos.

Laboratório 3: Semáforos

-------------------------------------------------------------------------------------------

Material Semaphore-Monitor  (Para estudar para a primeira prova)

Lista 1 (Monitores, Locks e Semáforos)

Responda no arquivo e devolva no Moodle em PDF.

Lista 2 (Equivalência Serial de Transações e Locks)

Prova 1  

Prova 1 (com respostas)

-------------------------------------------------------------------------------------------

Unidade 4:  Sockets.  Ver slides do Prof. Lau 

Cap.4 (Coulouris) Communicação Inter-Processos

Comunicação por Sockets

Programação com Sockets.  Implementar uma aplicação distribuída usando sockets.

(1) Comunicação de Datagramas UDP
Exemplo 1:  UDPClient1 x UDPServer1
Exemplo 2:  UDPClient2 x UDPServer2
Exemplo 3:  UDPClient3 x UDPServer3

(2) Comunicação com TCP Streams
Exemplo 2:  TCPClient2 x TCPServer2

(3) Comunicação de grupo com IP Multicast.

API Abstration Multicast  (contém alguns slides sobre Multicast)

Multicasting in Java (contém explicação sobre Multicast em Java)

Livro de Sistemas Distribuidos (Coulouris)

Laboratório 4.1  (Tarefa proposta pelo Prof. Bosco): Tarefas com Sockets

Exemplos de Código para implementação e datagramas com sockets.
   Datagram Socket Client
   Datagram Socket Server

Exemplos e Código para implementação e sockets TCP
   TCP Stream Socket Client
   TCP Stream Socket Server

Exemplo de código para implementação de comunicação de grupo com multicast IP.
   Group Communication Socket com IP Multicast

Laboratório 4.2 (Tarefa proposta pelo Prof. Lau) : Comunicação por Sockets

-------------------------------------------------------------------------------------------

Unidade 5 - Objetos Distribuidos com Java RMI

Tarefa 5.1 RMI:   Calculadora (preliminar, feito em aula)

Objetos Distribuídos: RPC, CORBA, Java RMI

Objetos Distribuídos e Invocação Remota

Tarefa 5.2 - Questionário: Objetos Distribuídos e Invocação Remota de Métodos


Tarefa 5.3 -  Usando o Padrão de Projeto "Factory"

Laboratório 5.4 (Bosco)   Implementando CallBack
          
Implementar usando sockets ou Java RMI, uma aplicação distribuída   
           sobre o sistema de despertador automático de uma operadora de
           telefonia.  Um design pattern (padrão de projeto) está descrito no
           arquivo texto no link abaixo. Você deve utilizar este design pattern,
           podendo se discordar do código usado, refazer conforme seu
           entendimento.

Laboratório 5.5  Eventos e Notificações  (Invocação de Métodos Remotos)

Prova 2 - 05/07/2011

Prova de Recuperação:   12/07/2011 

-------------------------------------------------------------------------------------------