MULTITHREADING Prof.: Michele Nasu Tomiyama Bucci Introdução • O corpo humano realiza uma grande variedade de operações paralelamente, ou concorrentemente. • Os computadores também realiza operações concorrentemente. Porém, apenas computadores que têm múltiplos processadores podem de fato executar operações concorrentemente. • Em computadores de um único processador, os sistemas operacionais utilizam várias técnicas para simular concorrência, mas uma única operação pode executar por vez. Introdução • A maioria das linguagens de programação não permitem que programadores especifiquem atividades concorrentes. • Em geral, elas fornecem apenas instruções de controle que permitem que os programadores realizem uma ação por vez, avançando para a próxima ação depois de a anterior ser concluída. • O Java disponibiliza a concorrência para o programador de aplicativos por meio de suas APIs. • O programador especifica os aplicativos que contém threads de execução, em que cada thread designa uma parte do programa que pode executar concorrentemente com outras threads – multithreading. Introdução • Um exemplo de multithreading é a coleta de lixo do Java. • Linguagens como C/C++ exigem que o programador reivindique memória dinamicamente alocada (free) de modo explícito (malloc). • O Java fornece uma thread coletora de lixo que reivindica a memória que não é mais necessária. Classe Thread • A qualquer dado momento, uma thread pode estar em de seus vários estados de thread: Estados da Thread • Uma nova thread inicia seu ciclo de vida no estado novo. Ela permanece nesse estado até o programa iniciar a thread, o que a coloca em estado executável (a thread está executando a sua tarefa). • Às vezes uma thread entra no estado de espera enquanto espera outra thread realizar uma tarefa. Uma vez nesse estado, a thread só volta ao estado executável quando outra thread sinalizar a thread de espera para retornar a execução. Estados da Thread • Uma thread executável também pode entrar no estado de espera sincronizada por um intervalo especificado de tempo. Uma thread nesse estado volta ao estado executável quando esse intervalo de tempo expira ou quando ocorre um evento que ele está esperando. • As threads em espera sincronizada não podem utilizar um processador, mesmo que haja um disponível. • Uma thread pode transitar para o estado de espera sincronizada se fornece um intervalo de espera opcional quando ela estiver esperando outra thread realizar uma tarefa. Estados da Thread • Outra maneira de colocar uma thread em espera sincronizada é colocá-la para dormir. Uma thread adormecida permanece no estado de espera sincronizada por um período determinado de tempo (intervalo de adormecimento). • As threads dormem quando, por um breve período, não têm de realizar nenhuma tarefa. • Uma thread entra no estado terminado quando completa sua tarefa ou termina (condição de erro). • Exemplo: Processador de texto. Estados da Thread • No nível do sistema operacional, o estado executável do Java na realidade inclui 2 estados separados: pronto e executando. Estados da Thread • Quando uma thread pela 1ª vez no estado executável a partir do estado novo, a thread está no estado pronto. • Uma thread pronta entra no estado de execução quando o sistema operacional atribui a thread a um processador. – despachar uma thread. • Na maioria dos sistemas operacionais, cada thread recebe uma pequena quantidade de tempo de processador – quantum ou fração de tempo – com a qual realiza sua tarefa. Estados da Thread • Quando o quantum de uma thread expira, a thread retorna ao estado pronto e o sistema atribuirá outra thread ao processador. • O processo que utiliza o sistema operacional para decidir qual thread despachar é conhecido como agendamento de thread e depende das prioridades de threads. Prioridades de Thread e Agendamento de Thread • Cada thread Java tem uma prioridade que ajuda o sistema operacional a determinar a ordem em que as threads são agendadas. • MIN_PRIORITY(constante de 1). • MAX_PRIORITY(constante de 10). • NORM_PRIORITY(constante de 5). • Informalmente as threads com prioridade mais alta são mais importantes para um programa e devem se alocadas em tempo de processador antes das threads de prioridade mais baixa. • Entretanto, as prioridades de thread não podem garantir a ordem em que elas são executadas. Prioridades de Thread e Agendamento de Thread • O trabalho de um scheduler de thread de sistema operacional é determinar a próxima thread que entra em execução. • Uma simples implementação do scheduler de thread mantém a thread de prioridade mais alta executando o tempo todo e, se houver mais de uma thread de prioridade mais alta, isso assegura que casa uma dalas executa por um quantum no estilo rodízio. • As threads A e B vão ficar executando no estilo rodízio até que uma das threads torna-se pronta. • O processador então dedica-se a thread que resta. • Quando a thread termine a execução, a thread C executa até a sua conclusão ( considerando que nenhuma thread de prioridade mais alta chegará). • Após, D, E e F entra no rodízio de execução. Criando e Executando Threads • Existem duas formas de criar explicitamente criar explicitamente um thread em Java: • Estendendo a classe Thread e instanciando um objeto desta nova classe. • Implementando a interface Runnable, e passando um objeto desta nova classe como argumento do construtor da classe Thread. • Nos dois casos a tarefa a ser executado pelo thread deverá ser descrita pelo método run(). Criando e Executando Threads • A interface Runnable declara um único método chamado • • • • • run. Runnables são executados por um objeto de uma classe que implementa a interface Executor. Essa interface declara um único método chamado execute. Em geral, um objeto Executor cria e gerencia um grupo de threads denominado de pool de threads. A interface ExecutorService provê vários outros métodos para gerenciar o ciclo de vida de um Executor. O Executor atribui cada Runnable a uma das threads disponíveis do pool de threads. Exemplo • Faça um programa que imprima os números primos existentes entre 0 e 5000. Utilize threads. • Para cada faixa de 1000 valores crie uma thread e dispare o processo para cada uma delas public class PrimoThread implements Runnable{ private int min; private int max; public PrimoThread(int x, int y){ min = x; max = y; } public boolean primo(int x){ for(int i=2; i<x/2+1;i++){ if(x%i==0){ return false; } } return true; } public void run(){ //System.out.println("Primos entre: "+min+"-"+max); for(int i=min; i<=max;i++){ if(primo(i)){ System.out.println(i); } } System.out.println(""); } } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class PrimoThreadTeste { public static void main(String args[]){ PrimoThread p1 = new PrimoThread(1, 1000); PrimoThread p2 = new PrimoThread(1001, 2000); PrimoThread p3 = new PrimoThread(2001, 3000); PrimoThread p4 = new PrimoThread(3001, 4000); PrimoThread p5 = new PrimoThread(4001, 5000); System.out.println("Starting threads"); ExecutorService threadExecutor = Executors.newFixedThreadPool(5); threadExecutor.execute(p1); threadExecutor.execute(p2); threadExecutor.execute(p3); threadExecutor.execute(p4); threadExecutor.execute(p5); threadExecutor.shutdown(); //System.out.println("Threads Started, main ends"); } } Exemplo • Implemente uma Corrida de Sapos! • Crie um Classe sapo que herda de Runnable • Atributos: distanciaPercorrida, distanciaPulo... • Crie uma Classe Corrida de Sapos • Atributos: distanciaCorrida, NumSapos public class SapoThread implements Runnable{ private String nome; private double distPulo; private double distPercorrido; private double distMax; public SapoThread(String n, double pu, double pe, double dm){ nome=n; distPulo=pu; distPercorrido=pe; distMax=dm; } public void run(){ while(distPercorrido<distMax){ distPercorrido+=distPulo; System.out.println(nome+" Chegou!"); } } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SapoThreadTeste { public static void main(String args[]){ double dist = 10.0; SapoThread s1 = new SapoThread("Sapo1",0.5,0.0,dist); SapoThread s2 = new SapoThread("Sapo2",0.3,0.0,dist); SapoThread s3 = new SapoThread("Sapo3",0.9,0.0,dist); SapoThread s4 = new SapoThread("Sapo4",0.6,0.0,dist); SapoThread s5 = new SapoThread("Sapo5",0.4,0.0,dist); ExecutorService threadExecutor = Executors.newFixedThreadPool(5); threadExecutor.execute(s1); threadExecutor.execute(s2); threadExecutor.execute(s3); threadExecutor.execute(s4); threadExecutor.execute(s5); threadExecutor.shutdown(); } }