Concorrência e thread Petrônio Júnior(pglj) Márcio Neves(mmn2) Concorrência Concorrência ocorre devido à disputa de recursos compartilhados. Se dois clientes, por exemplo, tentam alterar um mesmo objeto, uma inconsistência pode ser gerada. Sincronização – o que é? Em uma aplicação distribuída, vários computadores com diferentes capacidades de processamento e diferentes conexões de rede trabalham em conjunto. Devido a essas diferenças, eventos que deveriam ocorrer em determinada ordem são observados fora da ordem esperada. Por isso é necessário algum meio de sincronização. Alguns problemas com Concorrência Não-determinismo: x = 1 || x = 2 Podemos não saber ao certo qual o valor correto. Dependência de Velocidade: [[ f(); x = 1 ]] || [[ g(); x = 2 ]] O valor final de x depende de qual das funções, f() e g(), terminar primeiro Alguns problemas com Concorrência Starvation: Processo de baixa prioridade precisa de um recurso que nunca é fornecido a ele Deadlock: Dois processos bloqueiam a sua execução pois um precisa de um recurso bloqueado pelo outro processo Como Resolver? Linguagens de programação geralmente oferecem primitivas para evitar a ocorrência desses problemas Principais recursos para evitar a concorrência: Eventos Semáforos Monitores Mensagens Eventos – O que são? Modelam uma ocorrência do sistema Primitivas: wait(x): Espera (bloqueia o processo até) que ocorra um certo evento “x” signal(x): Gera uma ocorrência do evento “x”, reativando todos os processos que foram bloqueados por um wait anterior. Semáforos – O que são? Variáveis que indicam se um recurso está disponível para uso ou não. Operações Principais: initialize(x,n): Inicializa o semáforo “x” e garante que no máximo “n” processos utilizarão esse recurso. Se algum outro tentar utilizá-lo, não será permitido. Semáforos – O que são? wait(x): Obtém um recurso do semáforo “x”. Se nenhum estiver disponível, o processo é bloqueado signal(x): Libera um recurso do semáforo “x”. Se algum processo estiver bloqueado, ou seja, se tentou obter certo recurso e este não estava disponível causando o seu bloqueio, então esse processo será liberado e autorizado a usar o recurso. Monitores – O que são? Apenas um processo pode executar funções em um determinado momento Em Java temos o modificador synchronized. Esse modificador faz com que um objeto que está sendo acessado só possa ser acessado novamente quando a tarefa que está sendo realizada sobre ele seja concluída. Mensagens – O que são? Define mensagens que são enviadas entre processos. Essas mensagens podem ser síncronas ou assíncronas. Primitivas: Send(x,p): Envia a mensagem “x” para o processo p. Receive(x): Espera até que uma mensagem “x” esteja disponível Test(): Verifica se existe uma mensagem disponível Thread Um thread é um fluxo único de controle sequencial dentro de um programa É uma forma de um processo dividir a si mesmo em tarefas que passam a dividir o tempo que ele tem pra ser executado, executando, dessa forma, “simultaneamente”. Thread Um thread não é um programa, mas executa dentro de um programa. Thread O overhead causado pelo thread é menor que o do escalonamento de processos embora a mesma memória seja compartilhada. Além da memória, threads compartilham o estado da informação de processos. Quanto mais threads mais complicada a sincronia com a principal (se for requerida) Thread Em java, temos duas alternativas para implementar o recurso de multi-thread: a) Estendendo da Classe Thread public class Execucao { public static void main(String[] args) { Proc p = new Proc(); p.start(); while (true) { System.out.println("thread main executando");} }} class Proc extends Thread { public void run() { while (true) { System.out.println("thread executando");} }} Thread b) Implementando a Interface Runnable public class Execucao { public static void main(String[] args) { Proc p = new Proc(); Thread t = new Thread(p); t.start(); while (true) { System.out.println("thread main executando"); } } } class Proc implements Runnable { public void run() { while (true) {System.out.println("thread executando");} } } Thread O método main é uma thread e instancia um objeto p que também é instância de uma classe que estende de Thread. O método start() chamado, informa ao escalonador de thread que coloque na fila de execução a Thread p. A thread p não vai executar imediatamente quando o método start foi chamado, somente sinaliza o escalonador Socket Sockets são interfaces de programação que estão entre a camada de transportes e a aplicação. São como portas que possibilitam a comunicação entre componentes distribuídos, de aplicações, em uma rede de computadores. É um tipo de programação necessária para a criação de sistemas distribuídos como o RMI e o CORBA. Socket Processos em hosts distintos comunicam-se por meio de envio de mensagens enviadas e recebidas através de seu socket. Em uma aplicação distribuída pode-se enviar um fluxo de informações para outra através dos sockets. Ao utilizar sockets, o programador deve utilizar o tipo adequado ao protocolo de transporte utilizado: protocolo orientado à conexão (confiável) ou protocolo não orientado à conexão (não confiável), também conhecido como datagrama. Socket A linguagem Java oferece packages para a programação com Sockets e para a programação através de invocação remota de métodos ou RMI (Remote Method Invocation).Entretanto, também temos implementações de RPC e de Sockets para outras linguagem, como o CORBA (implementação do RPC para varias linguagens). Exemplo de uma aplicação Java que utiliza a programação em Socket para estabelecer uma conexão e enviar uma mensagem pela rede. Utiliza a arquitetura Cliente-Servidor. Socket – Classe Servidor import import public public java.io.*; java.net.*; class Servidor { static void main(String[] args) throws IOException { final String mensagem = "Oi! Tudo bem?"; ServerSocket socketServidor = new ServerSocket(8080); // criacao do socket no servidor System.out.println ("Servidor ativado. Aguardando na porta 8080...\n"); Socket socketCliente = socketServidor.accept( ); // servidor aguarda cliente OutputStream fluxoSaiSocket = socketCliente.getOutputStream( ); // define fluxo de saida PrintWriter saida = new PrintWriter(fluxoSaiSocket, true); InputStream fluxoEntSocket = socketCliente.getInputStream( ); // define fluxo de entrada BufferedReader entrada = new BufferedReader(new InputStreamReader(fluxoEntSocket)); String msgRecebida = entrada.readLine( ); // recebe mensagem do cliente System.out.println ("Mensagem Recebida: <" + msgRecebida + ">"); saida.println(mensagem); // envia mensagem para o cliente System.out.println ("Mensagem Enviada: <" + mensagem + ">\n"); socketCliente.close( ); socketServidor.close( ); saida.close( ); entrada.close( ); } } Socket – Classe Cliente import java.io.*; import java.net.*; public class cliente { public static void main (String[] args) throws IOException { final String mensagem = "Oi!"; try { Socket socketCliente = new Socket("localhost", 8080); // criacao do socket no cliente OutputStream fluxoSaiSocket = socketCliente.getOutputStream( ); // define fluxo de saida PrintWriter saida = new PrintWriter(fluxoSaiSocket, true); InputStream fluxoEntSocket = socketCliente.getInputStream( ); // define fluxo de entrada BufferedReader entrada = new BufferedReader(new InputStreamReader(fluxoEntSocket)); saida.println(mensagem); // envia mensagem para o servidor System.out.println("Mensagem Enviada: <" + mensagem + ">"); String msgRecebida = entrada.readLine( ); // recebe mensagem do servidor System.out.println ("Mensagem Recebida: <" + msgRecebida + ">\n"); socketCliente.close( ); saida.close( ); entrada.close( ); } catch(UnknownHostException e) { System.err.println("Host nao encontrado!"); System.exit(1); } catch(java.io.IOException e) { System.err.println("Conexao nao pode ser estabelecida!"); System.exit(1); }