Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
} } class TesteA { private int v; public void f() { v++; System.out.println("Valor de v: "+v); }
} A sada seria:
de de de de de
v: v: v: v: v:
1 2 3 4 5
Vejamos agora um exemplo contendo duas linhas de execuo. Para diferenciar a chamada normal da chamada via Thread colocamos um parmetro no mtodo f(String x). Em primeiro lugar lanamos o mtodo f da classe TesteA, usando a construo Thread. Esta execuo ir concorrer com as chamadas consecutivas e sequenciais normais do mtodo f:
// SequencialA.java import java.lang.Thread; public class SequencialA extends Object { public static void main(String args[]) throws Exception { int i; TesteA TA = new TesteA(); TesteA TTA = new TesteA(); Thread thA = new Thread(TTA); thA.start(); for (i=0; i<5; i++) { TA.f("normal"); Thread.sleep(1000); } } }
class TesteA implements Runnable { private int v; public void run() { f("run"); } public void f(String x) { v++; System.out.println(x+" Valor de v: "+v);
Threads e Sockets em Java MAC448 - BCC - Marcilio
} }
A saida seria: normal Valor run Valor de normal Valor normal Valor normal Valor normal Valor de v: de de de de v: 1 v: v: v: v: 1 2 3 4 5
Note que a chamada via thread entra no meio da saida das chamadas normais. A interface Runnable O exemplo acima mostra a primeira forma de lanar um thread, implementando a interface Runnable. Toda classe que implementa a interface Runnable deve especificar um mtodo cuja assinatura public void run(), executado no momento em que a linha de execuo inicializada. Os comandos abaixo cria uma nova linha de execuo: TesteA TTA = new TesteA(); Thread thA = new Thread(TTA); thA.start(); Outro exemplo com a interface Runnable No exemplo abaixo lanamos 3 vezes alternadamente o mtodo f via Thread e via normal:
// SequencialB.java import java.lang.Thread; public class SequencialB extends Object { public static void main(String args[]) throws Exception { TesteA TA = new TesteA(); TesteA TB = new TesteA(); TesteA TC = new TesteA(); TesteA TTA = new TesteA(); Thread thA = new Thread(TTA); thA.start(); TA.f("normal"); Thread.sleep(500); TesteA TTB = new TesteA(); Thread thB = new Thread(TTB); thB.start();
Threads e Sockets em Java MAC448 - BCC - Marcilio
TB.f("normal"); Thread.sleep(500); TesteA TTC = new TesteA(); Thread thC = new Thread(TTC); thC.start(); TC.f("normal"); Thread.sleep(500);
} }
class TesteA implements Runnable { private int v; public void run() { f("run"); } public void f(String x) { v++; System.out.println(x+" Valor de v: "+v); } }
A saida seria: normal Valor run Valor de normal Valor run Valor de normal Valor run Valor de de v: de v: de v: v: 1 1 v: 1 1 v: 1 1
Porque a varivel v tem valor 1 para todas as chamadas? Estendendo a classe Thread A segunda forma de utilizar threads em Java estender a prpria classe Thread, presente no pacote java.lang.Thread. Da mesmo jeito que a anterior deve estar presente o mtodo public void run(), que chamado sempre que uma linha de execuo criada para o objeto. O exemplo abaixo o mesmo que o SequencialA acima e a saida a mesma:
// SequencialC.java import java.lang.Thread; public class SequencialC extends Object { public static void main(String args[]) throws Exception { int i; TesteA TA = new TesteA();
Threads e Sockets em Java MAC448 - BCC - Marcilio
TesteA TTA = new TesteA(); TTA.start(); for (i=0; i<5; i++) { TA.f("normal"); Thread.sleep(1000); } } }
class TesteA extends Thread { private int v; public void run() { f("run"); } public void f(String x) { v++; System.out.println(x+" Valor de v: "+v); } }
Outro exemplo estendendo a classe Thread O exemplo abaixo o mesmo que o SequencialB acima e a saida a mesma:
// SequencialD.java import java.lang.Thread; public class SequencialD extends Object { public static void main(String args[]) throws Exception { TesteA TA = new TesteA(); TesteA TB = new TesteA(); TesteA TC = new TesteA(); TesteA TTA = new TesteA(); TTA.start(); TA.f("normal"); Thread.sleep(500); TesteA TTB = new TesteA(); TTB.start(); TB.f("normal"); Thread.sleep(500); TesteA TTC = new TesteA(); TTC.start(); TC.f("normal"); Thread.sleep(500);
} }
Mais um exemplo No exemplo abaixo em vez de esperar algum tempo (Thread.sleep), o mtodo ff usa efetivamente a CPU contando at 1, 2 e 3 milhes. So lanados 3 threads. Cada um deles interrompido no meio das contagens produzindo uma sada do tipo: chamada 2 - contei ate 100.000.000 chamada 1 - contei ate 100.000.000 chamada 3 - contei ate 100.000.000 chamada 3 - contei ate 200.000.000 chamada 2 - contei ate 200.000.000 chamada 1 - contei ate 200.000.000 chamada 2 - contei ate 300.000.000 chamada 3 - contei ate 300.000.000 chamada 1 - contei ate 300.000.000
// Paralelo.java import java.lang.Thread; public class Paralelo extends Object { public static void main(String args[]) throws Exception { Loops La = new Loops("chamada 1"); Loops Lb = new Loops("chamada 2"); Loops Lc = new Loops("chamada 3"); // Lana em paralelo os contadores La.start(); Lb.start(); Lc.start();
} }
// metodo construtor public Loops(String x) { st = x; } // metodo chamado pelo xx.start() public void run() { ff(); } // metodo que conta public void ff() { int i; // conta ate 100.000.000 for (i=0;i<100000000;i++){} System.out.println("\n"+st+" - contei ate 100.000.000"); // conta ate 200.000.000 for (i=0;i<200000000;i++){} System.out.println("\n"+st+" - contei ate 200.000.000"); // conta ate 300.000.000 for (i=0;i<300000000;i++){} System.out.println("\n"+st+" - contei ate 300.000.000"); } }
// Socket, que uma porta da comunicao. System.out.print("Esperando algum se conectar..."); Socket conexao = s.accept(); System.out.println(" Conectou!"); // obtendo os objetos de controle do fluxo de comunicao BufferedReader entrada = new BufferedReader(new InputStreamReader(conexao.getInputStream())); PrintStream saida = new PrintStream(conexao.getOutputStream()); // esperando por alguma string do cliente at que ele // envie uma linha em branco. // Verificar se linha recebida no nula. // Isso ocorre quando conexo interrompida pelo cliente // Se a linha no for null(o objeto existe), podemos usar // mtodos de comparao de string(caso contrrio,estaria // tentando chamar um mtodo de um objeto que no existe) String linha = entrada.readLine(); while (linha != null && !(linha.trim().equals(""))) { // envia a linha de volta. saida.println("Eco: " + linha); // espera por uma nova linha. linha = entrada.readLine(); } // se o cliente enviou linha em branco, fecha-se conexo. conexao.close(); // e volta-se ao loop, esperando mais algum se conectar } } catch (IOException e) { // caso ocorra alguma excesso de E/S, mostre qual foi System.out.println("IOException: " + e); } } } Vamos agora ao cliente correspondente. // ClienteDeEco.java import java.io.*; import java.net.*; public class ClienteDeEco { public static void main(String args[]) { try { // para se conectar ao servidor, cria-se objeto Socket. // O primeiro parmetro o IP ou endereo da mquina que
Threads e Sockets em Java MAC448 - BCC - Marcilio
// se quer conectar e o segundo a porta da aplicao. // Neste caso, usa-se o IP da mquina local (127.0.0.1) // e a porta da aplicao ServidorDeEco (2000). Socket conexao = new Socket("127.0.0.1", 2000); // uma vez estabelecida a comunicao, deve-se obter os // objetos que permitem controlar o fluxo de comunicao BufferedReader entrada = new BufferedReader(new InputStreamReader(conexao.getInputStream())); PrintStream saida = new PrintStream(conexao.getOutputStream()); String linha; // objetos que permitem a leitura do teclado BufferedReader teclado = new BufferedReader(new InputStreamReader(System.in)); // loop principal while (true) { // l a linha do teclado System.out.print("> "); linha = teclado.readLine(); // envia para o servidor saida.println(linha); // pega o que o servidor enviou linha = entrada.readLine(); // Verifica se linha vlida, pois se for null a conexo // foi interrompida. Se ocorrer isso, termina a execuo. if (linha == null) { System.out.println("Conexo encerrada!"); break; } // se a linha no for nula, deve-se imprimi-la no vdeo System.out.println(linha); } } catch (IOException e) { // caso ocorra alguma excesso de E/S, mostre qual foi. System.out.println("IOException: " + e); } } } Qual o problema na soluo acima? Apenas um cliente por vez pode se conectar ao servidor. Imagine agora que em vez de um servidor de eco, tivssemos um servidor de chat (bate papo). Vrios clientes tinham que estar conectados ao mesmo tempo no servidor.
Para que isso possa ocorrer, a soluo usar a Thread. A linha de execuo inicial dispara outra linha a cada novo cliente e fica esperando por novas conexes. Um servidor e cliente de chat Vamos modificar o servidor/cliente de eco acima, para um servidor/cliente de chat. O servidor de chat deve aceitar conexo de um cliente, disparar uma thread para atender esse cliente e esperar por conexo de um novo cliente. A thread que atende um cliente especfico deve esperar que este envie uma mensagem e replicar esta mensagem para todos os clientes conectados. Quando esse cliente desconectar a thread deve avisar a todos os clientes conectados que isso ocorreu. Portanto, necessrio que o servidor guarde em um vetor, todos os clientes conectados num dado instante. // ServidorDeChat.java import java.io.*; import java.net.*; import java.util.*; public class ServidorDeChat extends Thread { public static void main(String args[]) { // instancia o vetor de clientes conectados clientes = new Vector(); try { // criando um socket que fica escutando a porta 2222. ServerSocket s = new ServerSocket(2222); // Loop principal. while (true) { // aguarda algum cliente se conectar. A execuo do // servidor fica bloqueada na chamada do mtodo accept da // classe ServerSocket. Quando algum cliente se conectar // ao servidor, o mtodo desbloqueia e retorna com um // objeto da classe Socket, que porta da comunicao. System.out.print("Esperando alguem se conectar..."); Socket conexao = s.accept(); System.out.println(" Conectou!"); // cria uma nova thread para tratar essa conexo Thread t = new ServidorDeChat(conexao); t.start(); // voltando ao loop, esperando mais algum se conectar. } } catch (IOException e) { // caso ocorra alguma excesso de E/S, mostre qual foi. System.out.println("IOException: " + e); }
Threads e Sockets em Java MAC448 - BCC - Marcilio
} // Parte que controla as conexes por meio de // Note que a instanciao est no main. private static Vector clientes; // socket deste cliente private Socket conexao; // nome deste cliente private String meuNome; // construtor que recebe o socket deste cliente public ServidorDeChat(Socket s) { conexao = s; } // execuo da thread public void run() { try { // objetos que permitem controlar fluxo de comunicao BufferedReader entrada = new BufferedReader(new InputStreamReader(conexao.getInputStream())); PrintStream saida = new PrintStream(conexao.getOutputStream()); // primeiramente, espera-se pelo nome do cliente meuNome = entrada.readLine(); // agora, verifica se string recebida valida, pois // sem a conexo foi interrompida, a string null. // Se isso ocorrer, deve-se terminar a execuo. if (meuNome == null) {return;} // Uma vez que se tem um cliente conectado e conhecido, // coloca-se fluxo de sada para esse cliente no vetor de // clientes conectados. clientes.add(saida); // clientes objeto compartilhado por vrias threads! // De acordo com o manual da API, os mtodos so // sincronizados. Portanto, no h problemas de acessos // simultneos. // Loop principal: esperando por alguma string do cliente. // Quando recebe, envia a todos os conectados at que o // cliente envie linha em branco. // Verificar se linha null (conexo interrompida) // Se no for nula, pode-se compar-la com mtodos string String linha = entrada.readLine(); while (linha != null && !(linha.trim().equals(""))) { // reenvia a linha para todos os clientes conectados sendToAll(saida, " disse: ", linha);
Threads e Sockets em Java MAC448 - BCC - Marcilio
threads.
// espera por uma nova linha. linha = entrada.readLine(); } // Uma vez que o cliente enviou linha em branco, retira-se // fluxo de sada do vetor de clientes e fecha-se conexo. sendToAll(saida, " saiu ", "do chat!"); clientes.remove(saida); conexao.close(); } catch (IOException e) { // Caso ocorra alguma excesso de E/S, mostre qual foi. System.out.println("IOException: " + e); } } // enviar uma mensagem para todos, menos para o prprio public void sendToAll(PrintStream saida, String acao, String linha) throws IOException { Enumeration e = clientes.elements(); while (e.hasMoreElements()) { // obtm o fluxo de sada de um dos clientes PrintStream chat = (PrintStream) e.nextElement(); // envia para todos, menos para o prprio usurio if (chat != saida) {chat.println(meuNome + acao + linha);} } } } O cliente deve aguardar o usurio digitar uma mensagem no teclado e enviar essa mensagem ao servidor. Mas no to simples assim. H um problema: mensagens podem chegar a qualquer momento do servidor e devem ser mostradas no vdeo. Se voc pensou tambm em thread, acertou. Uma thread lanada no incio e fica esperando qualquer mensagem do servidor para apresent-la no vdeo. A linha de execuo principal do cliente se encarrega de esperar uma mensagem digitada pelo usurio e envi-la para o servidor. // ClienteDeChat.java import java.io.*; import java.net.*; public class ClienteDeChat extends Thread { // Flag que indica quando se deve terminar a execuo. private static boolean done = false; public static void main(String args[]) { try { // Para se conectar a algum servidor, basta se criar um
Threads e Sockets em Java MAC448 - BCC - Marcilio
// objeto da classe Socket. O primeiro parmetro o IP ou // o endereo da mquina a qual se quer conectar e o // segundo parmetro a porta da aplicao. Neste caso, // utiliza-se o IP da mquina local (127.0.0.1) e a porta // da aplicao ServidorDeChat. Nada impede a mudana // desses valores, tentando estabelecer uma conexo com // outras portas em outras mquinas. Socket conexao = new Socket("127.0.0.1", 2222); // uma vez estabelecida a comunicao, deve-se obter os // objetos que permitem controlar o fluxo de comunicao PrintStream saida = new PrintStream(conexao.getOutputStream()); // enviar antes de tudo o nome do usurio BufferedReader teclado = new BufferedReader(new InputStreamReader(System.in)); System.out.print("Entre com o seu nome: "); String meuNome = teclado.readLine(); saida.println(meuNome); // Uma vez que tudo est pronto, antes de iniciar o loop // principal, executar a thread de recepo de mensagens. Thread t = new ClienteDeChat(conexao); t.start(); // loop principal: obtendo uma linha digitada no teclado e // enviando-a para o servidor. String linha; while (true) { // ler a linha digitada no teclado System.out.print("> "); linha = teclado.readLine(); // antes de enviar, verifica se a conexo no foi fechada if (done) {break;} // envia para o servidor saida.println(linha); } } catch (IOException e) { // Caso ocorra alguma excesso de E/S, mostre qual foi. System.out.println("IOException: " + e); } } // parte que controla a recepo de mensagens deste cliente private Socket conexao; // construtor que recebe o socket deste cliente
Threads e Sockets em Java MAC448 - BCC - Marcilio
public ClienteDeChat(Socket s) { conexao = s; } // execuo da thread public void run() { try { BufferedReader entrada = new BufferedReader (new InputStreamReader(conexao.getInputStream())); String linha; while (true) { // pega o que o servidor enviou linha = entrada.readLine(); // verifica se uma linha vlida. Pode ser que a conexo // foi interrompida. Neste caso, a linha null. Se isso // ocorrer, termina-se a execuo saindo com break if (linha == null) { System.out.println("Conexo encerrada!"); break; } // caso a linha no seja nula, deve-se imprimi-la System.out.println(); System.out.println(linha); System.out.print("...> "); } } catch (IOException e) { // caso ocorra alguma exceo de E/S, mostre qual foi. System.out.println("IOException: " + e); } // sinaliza para o main que a conexo encerrou. done = true; } }