Você está na página 1de 38

Programação Concorrente

e Distribuída
Aula 4 – Memória Compartilhada

Universidade Católica de Brasília Programação Concorrente e Distribuída


Ciência da Computação
public class atividade1 {
public static void main(String[] args) {
Thread[] threads;
Atividade 1
threads = new Thread[5];
threadAtividade1 myRunnable = new threadAtividade1(); public class threadAtividade1 implements Runnable {
for(int i=0;i<5;i++) { private boolean doStop = false;
threads[i]= new Thread(myRunnable); public synchronized void doStop() {
threads[i].start(); this.doStop = true;
} }
boolean flag = true; private synchronized boolean keepRunning() {
int count = 0;
return this.doStop == false;
while(flag && count<5) {
flag = false; }
for(int i=0;i<5;i++) { public void run() {
System.out.println("Teste da thread"+ Thread t = Thread.currentThread();
i+":"+threads[i].isAlive()); while(keepRunning()) {
if(threads[i].isAlive()) int x = (int)(Math.random()*3000);
flag = true; try {
} Thread.sleep(x);
try { Thread.sleep(5000L); } } catch (InterruptedException e) {
catch (InterruptedException e) {
e.printStackTrace();
e.printStackTrace();
} }
count+=1; if(x%5==0) {
} System.out.println("End of thread: "+t.getName()+" x="+x);
System.out.println("Fim da espera."); break;
for(int i=0;i<5;i++) { }
if (threads[i].isAlive()) { else
System.out.println("Thread "+i+ System.out.println("Running thread: "+t.getName()+" x="+x+
" ainda viva"); " time:"+ZonedDateTime.now().toInstant().toEpochMilli());
}
}
}
myRunnable.doStop(); System.out.println("Stopping thread: "+t.getName());
} }
} }
ExecutorService
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

• ExecutorService é uma public class SimpleThreadPool {


classe para pool de public static void main(String[] args) {
threads. ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
• Você instancia dizendo Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
quantas threads vão rodar }
simultaneamente, daí vai executor.shutdown();
while (!executor.isTerminated()) {
adicionando teus Runnable }
// fica aqui enquanto está rodando as threads

nele e espera terminar. System.out.println("Finished all threads");


}
}
PARALELISMO EM JAVA

• Testar a aplicação:
• Teste o executor service na thread da atividade 1

Universidade Católica de Brasília


Ciência da Computação
PROGRAMAÇÃO CONCORRENTE

• Em alguns sistemas concorrentes, a comunicação entre os


componentes é implícita (escondida do programador),
enquanto em outros é explicita.
• A comunicação explícita pode ser dividida em duas classes:
• Memória compartilhada
• Troca de mensagens
• A troca de mensagens tende a ser mais simples que a
memória compartilhada
Memória Compartilhada

• Dois ou mais processos utilizam


a região de memória
compartilhada, conectando-a no
seu espaço de endereçamento
• Deve se ter a garantia de que
os dois processos não estejam
gravando dados no mesmo local
simultaneamente
MEMÓRIA COMPARTILHADA

• Atualização de dados compartilhados:


• Exemplo: Suponha X sendo compartilhado com valor inicial 0

• Qual o valor final de X?


MEMÓRIA COMPARTILHADA

• Então...
• Em situações em que os dados devem ser
compartilhados, cada processo é obrigado a se
preocupar com o que os outros estão fazendo
• Quando atualizam os dados compartilhados (seção
crítica do código) é necessário usar mecanismos que
garantam exclusão mútua
COMUNICAÇÃO ENTRE PROCESSOS

• Seção/Região Crítica:
• É uma área de código de um algoritmo que acessa um recurso
compartilhado que não pode ser acessado concorrentemente
por mais de um processo
• O objetivo é tornar a operação sobre o recurso compartilhado
atômica
• Alguns mecanismos de sincronização são necessários para
implementar a entrada e a saída de uma seção crítica para
assegurar o seu uso exclusivo
Threads e memória

• A lógica do aplicativo executada por threads envolve a computação


realizada na CPU, enquanto o resultado da computação é
armazenado na RAM.
• Cada thread aguardará sua vez de usar um núcleo da CPU para
realizar a computação e uma área de memória local (pilha e vários
quadros na pilha para cada chamada de método) para armazenar os
resultados da computação temporariamente.
• Depois que o thread conclui a execução do código, ele normalmente
libera o resultado de volta para a RAM (Heap).
Threads e memória

• Heap é a área de memória compartilhada entre threads onde todos


os objetos vivem.
• Stack (pilha) é a área de memória privada alocada para cada um das
threads em execução.
• Na memória heap o lixo é coletado (garbage collection) para liberar
espaço precioso removendo objetos que não são mais usados
​(referenciados) dentro do aplicativo
• O espaço de memória mantido pela pilha é liberado quando o thread
de execução é concluído
Threads e memória

• Heap
• Todos os objetos criados em um aplicativo Java têm espaço alocado na memória
chamado Heap.
• Esses objetos vivem enquanto são referenciados em algum lugar no aplicativo.
• Stack (Pilha)
• Threads executando um método ou invocando uma série de métodos precisariam
de espaço na memória para armazenar variáveis ​locais e argumentos de método -
esta área da memória é chamada de Stack.
• Cada método chamado pelo thread é empilhado sobre a chamada do método
anterior, chamada de “frames de pilha”.
Compartilhando memória
public class sharedmem {
public static void main(String[] args) {
entre threads vetorCalc vcalc = new vetorCalc(1);
Thread c1 = new Thread( () -> vcalc.add(10,0,10));
Thread c2 = new Thread( () -> vcalc.add(100,10,20));
• Diferentes threads c1.start();
c2.start();
podem compartilhar vcalc.mostra();
}
o mesmo objeto e
seus atributos static class vetorCalc{
int[] meuvetor = new int[20];
• Nesse exemplo o public vetorCalc(int i) {
Arrays.fill(meuvetor, i);
compartilhamento }
public void mostra() {
não gera for(int i:meuvetor)
System.out.println(i);
concorrência }
public void add(int a, int inicio, int fim) {
• Veremos mais for(int i=inicio;i<fim;i++)
adiante como lidar meuvetor[i] += a;
}
com a concorrência }
PARALELISMO EM JAVA

• Testar a aplicação:
• Mude o tamanho do vetor e divida
a operação em 3 threads

Universidade Católica de Brasília


Ciência da Computação
Compartilhando memória entre
public class sharedmem {
public static void main(String[] args) {

threads vetorCalc vcalc = new vetorCalc(1);


Thread c1 = new Thread( () -> vcalc.add(10,0,10));
Thread c2 = new Thread( () -> vcalc.add(100,10,20));
• Sincronização: c1.start();
c2.start();
• Note que a thread vcalc.mostra();
principal lança c1 e c2 }
static class vetorCalc{
e em seguida chama o int[] meuvetor = new int[20];
método mostra() public vetorCalc(int i) {
Arrays.fill(meuvetor, i);
• O que aconteceria se }
public void mostra() {
o processamento das for(int i:meuvetor)
threads fosse }
System.out.println(i);

demorado? public void add(int a, int inicio, int fim) {


try {
• Tente o código ao Thread.sleep(2000);
lado e veja } catch( InterruptedException e) {
System.out.println("thread foi interrompida"+e);
• O que aconteceu? }
for(int i=inicio;i<fim;i++)
meuvetor[i] += a;
}
}
Join

• A classe Thread fornece o método join () que permite


que uma thread espere até que outra thread conclua
sua execução.
• Se t for um objeto Thread cuja thread está atualmente
em execução, então t. join () garantirá que t seja
encerrado antes que a próxima instrução seja
executada pelo programa.
Compartilhando memória entre
public class sharedmem {
public static void main(String[] args) {

threads
vetorCalc vcalc = new vetorCalc(1);
Thread c1 = new Thread( () -> vcalc.add(10,0,10));
Thread c2 = new Thread( () -> vcalc.add(100,10,20));
• Sincronização: c1.start();
c2.start();
• Usando o join() para garantir que try {
c1.join();
as threads terminaram antes de } catch( InterruptedException e) {
System.out.println("c1 foi interrompida"+e);
continuar e usar seus resultados }
try {
static class vetorCalc{ c2.join();
int[] meuvetor = new int[20]; } catch( InterruptedException e) {
public vetorCalc(int i) { System.out.println("c2 foi interrompida"+e);
Arrays.fill(meuvetor, i); }
}
public void mostra() {
vcalc.mostra();
for(int i:meuvetor) }
System.out.println(i);
}
public void add(int a, int inicio, int fim) {
try {
Thread.sleep(2000);
} catch( InterruptedException e) {
System.out.println("thread foi interrompida"+e);
}
for(int i=inicio;i<fim;i++)
meuvetor[i] += a;
}
}
PARALELISMO EM JAVA

• Testar a aplicação com as 3 threads implementadas


no exercício anterior

Universidade Católica de Brasília


Ciência da Computação
PARALELISMO EM JAVA

• Na aplicação da calculadora as threads propostas não se sobrepõem


• Cada uma altera um pedaço do vetor
• Para simular o que pode acontecer se não houver controle sobre o
uso da memória compartilhada experimente o mesmo código com 3
threads todas alterando o vetor inteiro.
BENCHMARK

Definição:
Ato de executar um programa de computador, um conjunto de
programas ou outras operações, a fim de avaliar o desempenho relativo
de um objeto, normalmente executando uma série de testes padrões e
ensaios nele.

 Termo é comumente usado para os próprios programas


 Normalmente associado com performance de hardware

https://pt.wikipedia.org/wiki/Benchmark_(computa%C3%A7%C3%A3o)

Universidade Católica de Brasília


Ciência da Computação
PRECISÃO DO RELÓGIO EM JAVA
public class Relogio {
public static void main(String[] args) {
long tempo, inicio = System.currentTimeMillis();
....
tempo = System.currentTimeMillis() - inicio;
System.out.println("--> "+ tempo +" milisegundos");
}
}

Universidade Católica de Brasília Programação Concorrente e Distribuída


Ciência da Computação Prof. Edson Francisco da Fonseca
Medindo o tempo public class sharedmem {
public static void main(String[] args) {
long tempo, inicio = System.currentTimeMillis();
vetorCalc vcalc = new vetorCalc(1);
• Para medirmos os Thread c1 = new Thread( () -> vcalc.add(10,0,10));
ganhos com o Thread c2 = new Thread( () -> vcalc.add(100,10,20));
c1.start();
paralelismo precisamos c2.start();
try {
medir o tempo gasto na c1.join();
} catch( InterruptedException
solução do problema e) {
System.out.println("c1 foi interrompida"+e);
}
try {
static class vetorCalc{ c2.join();
int[] meuvetor = new int[20]; } catch( InterruptedException e) {
public vetorCalc(int i) { System.out.println("c2 foi interrompida"+e);
Arrays.fill(meuvetor, i); }
} vcalc.mostra();
public void mostra() { tempo = System.currentTimeMillis() - inicio;
for(int i:meuvetor)
System.out.println(i);
System.out.println("--> "+ tempo +" milisegundos");
} }
public void add(int a, int inicio, int fim) {
for(int i=inicio;i<fim;i++)
meuvetor[i] += a;
}
}
PARALELISMO EM JAVA

• Testar a aplicação
• Obtenha o tempo em
milissegundos para a operação de
soma do vetor por uma constante
num vetor de 1.000.000 de
posições usando 2, 5 e nenhuma
thread

Universidade Católica de Brasília


Ciência da Computação
AVALIAÇÃO DE DESEMPENHO

Speedup:
 Comparar desempenho entre dois sistemas, ou duas versões de um
mesmo sistema
 Relação entre os tempos de execução
 “Com uso de buffers temos um speedup de 1,2”

𝑇𝑇1 𝑇𝑇𝑠𝑠𝑒𝑒𝑟𝑟𝑖𝑖𝑎𝑎𝑙𝑙
𝑆𝑆 = 𝑆𝑆𝑝𝑝 =
𝑇𝑇2 𝑇𝑇𝑝𝑝𝑎𝑎𝑟𝑟𝑎𝑎𝑙𝑙𝑒𝑒𝑙𝑙𝑜𝑜
Universidade Católica de Brasília
Ciência da Computação
PARALELISMO EM JAVA

• Testar a aplicação
• Obtenha o speedup para a
operação de soma do vetor por
uma constante num vetor de
1.000.000 de posições usando 2, 5
e 10 threads

Universidade Católica de Brasília


Ciência da Computação
PARALELISMO EM JAVA

Prioridade da Thread:
 Valor de 1 a 10; 5 é o default padrão
 A thread herda a prioridade da thread que a
criou
 Modificada com setPriority(int)
 Obtida com getPriority()
 Escalonamento: Threads com prioridades
iguais são escalonadas em round-robin, com
cada uma ativa durante um quantum.

Universidade Católica de Brasília


Ciência da Computação
public class ThreadExample extends Thread {
public void run() {
// Imprime o nome da thread
System.out.println("Inside : " + Thread.currentThread().getName());
}
public static void main(String[] args) {
// Cria nova thread
Thread thread = new ThreadExample();
// lança a thread
thread.start();
// muda o nome da thread1
thread.setName("child thread xyz");
// define a prioridade da thread
thread.setPriority(5);
// Buscando uma instância deste encadeamento, ou seja, uma referência ao encadeamento que está em execução no momento.
Thread t = Thread.currentThread();
// nome da Thread
System.out.println(“Nome thread principal:”+t.getName());
System.out.println("Thread1 ID: " + thread.getId());

// prioridade e estado da thread


System.out.println("Priority of thread = " + thread.getPriority());
System.out.println(thread.getState());
}
}
PARALELISMO EM JAVA
• Testar a aplicação
• Crie um classe Runnable que tenha uma array de
100.000 posições inicializado com valores aleatórios
entre 0 e 1.000.000
• Nessa classe o método run() deve procurar o maior
número do array. Quando encontrar deve imprimir esse
número, o timestamp e seu id
• Crie um objeto da sua classe Runnable
• Lance 10 threads com prioridades variando de 1 a 10
executando sua classe Runnable
• Qual thread terminou primeiro?

Universidade Católica de Brasília


Ciência da Computação
AVALIAÇÃO DE DESEMPENHO
Monitoramento de Desempenho
 Existem medidas internas e externas à aplicação paralela

Instrumentação
 Para monitoramos o comportamento específico devemos modificar o
código-fonte para coletar dados de desempenho

Tracing
 Técnica que registra a entrada e saída do processador em regiões
do código-fonte

Precisão do Relógio
 Medição de tempo usa primitivas para ler o valor do relógio em
instantes específicos
Universidade Católica de Brasília Programação Concorrente e Distribuída
Ciência da Computação Prof. Edson Francisco da Fonseca
PROCESSADOR MULTICORE
Definição:
É o processador que tem dois ou mais núcleos de processamento
(cores) no interior de um único chip

 Cores são responsáveis por dividir as tarefas = multitarefa


 Não somam a capacidade de processamento, dividem tarefas
 SO trata cada core como um processador
 Normalmente, cada core possui seu próprio cache
 Necessário devido à dificuldade de resfriar singlecore

https://pt.wikipedia.org/wiki/Processador_multin%C3%BAcleo

Universidade Católica de Brasília


Ciência da Computação
PROCESSADOR MULTICORE

Vantagens:
 Resfriamento
 Economia de energia
 Licença de software

Desvantagens:
 Ajustes no SO
 Ajustes do software

Universidade Católica de Brasília


Ciência da Computação
PROCESSADORES...

Hyperthreading (Intel)
 A CPU expõe dois contextos de execução por núcleo físico. Isso significa que
um núcleo físico agora funciona como dois “núcleos lógicos" que podem lidar
com diferentes threads de software.
https://www.intel.com.br/content/www/br/pt/gaming/resources/hyper-
threading.html
Turbo Boost (Intel) e Turbo Core (AMD)
 Aumentam a frequência do core durante pequenos intervalos de tempo sempre
que o processador não estiver quente demais
 Em alguns casos desligam alguns cores e aumentam a frequência dos restantes

Universidade Católica de Brasília


Ciência da Computação
Acompanhando a carga nos cores

• No Windows:
• Inicie o gerenciador de tarefa
• Escolha a aba de desempenho
• Clique com o botão direito do
mouse no gráfico da CPU
• Escolha “Alterar gráfico >>
processadores lógicos
Testando

Execute seu programa multithread do exercício


anterior e veja o uso dos cores da máquina
PROGRAMAÇÃO PARALELA

Perguntas?
Universidade Católica de Brasília
Ciência da Computação
Obrigado!!!

Prof. Edson F. da Fonseca


edsonf@p.ucb.br

Universidade Católica de Brasília


Ciência da Computação
PARALELISMO EM JAVA

Atividade!

Universidade Católica de Brasília


Ciência da Computação
ATIVIDADE
• Implemente em Java aplicações paralelas, usando threads e
memória compartilhada para os problemas a seguir
• Divida o problema entre as threads.
• Teste com quantidades diferentes de threads (5, 10, 20, 50) e
registre os tempos de execução.
• Coloque os resultados numa planilha:
1. some os números de um intervalo.
2. some os números primos de um intervalo.
3. identifique o maior valor de um vetor.

Sempre trabalhar com valores

GRANDES
Universidade Católica de Brasília
Ciência da Computação

Você também pode gostar