Coleções, Genéricos, Threads Marco Antonio. Collection Principais métodos da interface...
Transcript of Coleções, Genéricos, Threads Marco Antonio. Collection Principais métodos da interface...
Coleções, Genéricos, ThreadsMarco Antonio
Collection • Principais métodos da
interface Collection.
Map• Principais métodos da
interface Map.
Hierarquia de Collecion
Ordenação• Uma classe é ordenada se pode ser iterada
pelos seus elementos em uma ordem específica, através de um índice ou por exemplo pela ordem de inserção.
Classificação• Uma classe classificada, quando seus
elementos estão classificados por algum critério, como por exemplo, em ordem alfabética, crescente ou cronológica etc. Toda classe classificada é ordenada, já uma classe ordenada pode não ser classificada.
List• As classes que implementam a interface List,
relevam o índice, com isso podemos inserir um item no meio de uma lista.
• As classes que implementam a interface List são ordenadas por meio de um índice, isso permite o acesso a um elemento que se encontra no meio da lista, através de seu índice.
• É uma espécie de sequência para armazenamento de objetos.
List• O que precisamos saber é que o índice em
uma lista é relevante, toda lista é ordenada, ou seja, podemos iterar em uma ordem especifica, seja ela pela ordem de inserção ou pela ordem do índice.
• Lista não é classificada.
Características das implementações
Raw types• Quando um tipo genérico (como uma coleção) é usado sem o
parâmetro, chamamos de raw types.• A utilização de raw types não é recomendada, mas é essencial
para manter compatibilidade com código legado.• Em função disso aparece ao lado da coleção um warning
indicando que você não está utilizando o parâmetro de tipo.• Para evitar esse warning, utilize a sintaxe logo abaixo.
@SuppressWarnings("unchecked")public class TesteColecaoSemGenericos {{código da classe}}
Coleções sem genéricos• Apesar de nossa coleção ter apenas um tipo
de classe, para recuperarmos os objetos precisamos fazer um cast.
• Funciona perfeitamente, mas ainda pode ser melhorado.
Finalmente, coleções com genéricos• Com essa sintaxe, não precisamos mais fazer cast.
– Collection<Pessoa> lista = new ArrayList<Pessoa>();
• Uma consequência direta da aplicação de genéricos na nossa coleção é que restringimos a adição de objetos somente do tipo Pessoa.
TesteColecaoComGenericospackage com.javabasico.genericos;import java.util.*;public class TesteColecaoComGenericos { public static void main(String[] args) { Pessoa p1 = new Pessoa(); p1.setNome("Marco"); Pessoa p2 = new Pessoa(); p2.setNome("Diego");1. Collection<Pessoa> lista = new ArrayList<Pessoa>(); lista.add(p1); lista.add(p2);2. Iterator<Pessoa> ite = lista.iterator();3. while (ite.hasNext()) {4. Pessoa pessoa = ite.next(); } }}
Tipos parametrizados1. Declaração do tipo parametrizado da coleção.
2. Declaração do tipo parametrizado do iterator.
3. Loop normal (while).
4. Não precisa mais de conversão, o compilador já sabe que o objeto é do tipo Pessoa. Nem mesmo aceitaria outro tipo de classe.
TesteColecaoComGenericospackage com.javabasico.genericos;
import java.util.*;
public class TesteColecaoComGenericos { public static void main(String[] args) { Pessoa p1 = new Pessoa(); p1.setNome("Marco"); Pessoa p2 = new Pessoa(); p2.setNome("Diego"); Collection<Pessoa> lista = new ArrayList<Pessoa>(); lista.add(p1); lista.add(p2);1. for(Pessoa p : lista){2. System.out.println(p.getNome()); } }}
foreach • Melhoramento do for tradicional.• Não é mais necessário utilizar o iterator.
1. Para cada Pessoa dentro da lista...
2. Imprime o nome.
CaixaDeObjeto sem genéricos• Vamos dar uma olhada em um exemplo de
classe que utiliza Object, ou seja, não utiliza tipos parametrizados.
CaixaDeObjetopackage com.javabasico.genericos;
public class CaixaDeObjeto { private Object objeto;
public void adiciona(Object objetoAdicionado) { objeto = objetoAdicionado; }
public Object recuperaObjeto() { return objeto; }}
Conversão obrigatória• Sem a utilização de genéricos precisamos
sempre fazer conversões.– Integer valorRecuperado = (Integer)
caixa.recuperaObjeto();
• Não existe validação nenhuma em tempo de compilação já que a conversão é forçada.
TesteCaixaDeObjetopackage com.javabasico.genericos;
public class TesteCaixaDeObjeto { public static void main(String[] args) { CaixaDeObjeto caixa = new CaixaDeObjeto(); caixa.adiciona(new Integer(10)); Integer valorRecuperado = (Integer) caixa.recuperaObjeto(); System.out.println("Valor integer: " + valorRecuperado); }}
Erro de conversão• Nada impede o programador de converter
para um tipo errado.• Infelizmente esse problema só será
descoberto em tempo de execução.
TesteCaixaDeObjetopackage com.javabasico.genericos;
public class TesteCaixaDeObjeto { public static void main(String[] args) { CaixaDeObjeto caixa = new CaixaDeObjeto(); caixa.adiciona(new Integer(10)); String valorRecuperado = (String) caixa.recuperaObjeto(); System.out.println("Valor integer: " + valorRecuperado); }}
Mensagem de erro• A exceção lançada quando a conversão não pode
ser feita é essa a seguir.
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.javabasico.genericos.TesteCaixaDeObjeto.main(TesteCaixaDeObjeto.java:7)
Tipo parametrizado• Vamos atualizar nossa caixa de objetos com
um tipo parametrizado.• Esse tipo (T) será informado quando
criarmos um objeto do tipo CaixaDeObjeto, como uma variável.
• A mesma técnica pode ser aplicada a interfaces ou métodos.
CaixaDeObjetopackage com.javabasico.genericos;
1. public class CaixaDeObjeto<T> {2. private T objeto;
3. public void adiciona(T objetoAdicionado) { objeto = objetoAdicionado; }
4. public T recuperaObjeto() { return objeto; }}
CaixaDeObjeto• Declaração do tipo parametrizado.• A variável objeto agora é do tipo
parametrizado. Esse tipo será indicado mais adiante.
• O método adiciona só permite que você utilize o tipo T.
• Recupera o objeto convertido para o tipo T.
Testando• Quando criamos um objeto do tipo
CaixaDeObjeto devemos informar o tipo, conforme a sintaxe.
• A partir desse momento, não será mais necessário fazer cast.
TesteCaixaDeObjetopackage com.javabasico.genericos;
public class TesteCaixaDeObjeto { public static void main(String[] args) {1. CaixaDeObjeto<Integer> caixa = new CaixaDeObjeto<Integer>(); caixa.adiciona(new Integer(10)); System.out.println("Valor integer: " + caixa.recuperaObjeto()); }}
TesteCaixaDeObjeto1. Agora parametrizamos a caixa de objetos.
Essa caixa de objetos só aceitará objetos do tipo Integer.
• Qualquer outro tipo de dado será considerado como erro pelo compilador.
Outros tipos de objetos• Vamos criar mais uma caixa de objetos
parametrizada para o tipo Pessoa.
TesteCaixaDeObjetopackage com.javabasico.genericos;
public class TesteCaixaDeObjeto { public static void main(String[] args) { CaixaDeObjeto<Integer> caixa = new CaixaDeObjeto<Integer>(); caixa.adiciona(new Integer(10)); System.out.println("Valor integer: " + caixa.recuperaObjeto()); CaixaDeObjeto<Pessoa> caixaPessoa = new CaixaDeObjeto<Pessoa>(); Pessoa pessoa = new Pessoa(); pessoa.setNome("Marco"); caixaPessoa.adiciona(pessoa); System.out.println("Nome: " + caixaPessoa.recuperaObjeto().getNome()); }}
Nomenclatura (apenas sugerida)• E - Elemento (usado extensivamente no
Java Collections Framework) • K - Key • N - Number • T – Type (Classe) • V - Value • S,U,V etc. - 2nd, 3rd, 4th types
Mais tipos parametrizados• Com o próximo exemplo você pode ver que
métodos também aceitam tipos parametrizados.
CaixaDeObjetopackage com.javabasico.genericos;
public class CaixaDeObjeto<T> { private T objeto;
public void adiciona(T objetoAdicionado) { objeto = objetoAdicionado; }
public T recuperaObjeto() { return objeto; }
public <U> void verifica(U u) { System.out.println("T: " + objeto.getClass().getName()); System.out.println("U: " + u.getClass().getName()); }}
TesteCaixaDeObjetoInspecaopackage com.javabasico.genericos;
public class TesteCaixaDeObjetoInspecao {
public static void main(String[] args) {
CaixaDeObjeto<Integer> caixa = new CaixaDeObjeto<Integer>();
caixa.adiciona(new Integer(10));
caixa.verifica(new Pessoa());
caixa.verifica(new String("Alguma frase"));
}
}
Memoryimport java.util.Date;public class Memory { public static void main(String[] args) { Runtime rt = Runtime.getRuntime(); System.out.println("Memoria total: " + rt.totalMemory()); System.out.println("Memoria Antes: " + rt.freeMemory()); Date d = null; String s = null; for (int i = 0; i < 50000; i++) { d = new Date(); d = null; s = "TEsdfafsadfjasdkfajdçfas"; s = null; } System.out.println("Memoria Depois: " + rt.freeMemory()); rt.gc(); System.out.println("Memoria Final: " + rt.freeMemory()); }}
Threads• Também chamados de segmentos.
Execucaopublic 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.currentThread().getName() + " executando"); } }}
class Proc implements Runnable { public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " executando"); } }}
TesteThreadpublic class TesteThread { public static void main(String[] args) { Counter ct = new Counter(); ct.start(); System.out.println("The thread has been started"); }}
class Counter extends Thread { public void run() { for (int i = 1; i <= 50; i++) { System.out.println("Count: " + i); } }}
TesteThreadpublic class TesteThread { public static void main(String[] args) { Counter ct = new Counter(); ct.run(); System.out.println("The thread has been started"); }}
class Counter extends Thread { public void run() { for (int i = 1; i <= 5; i++) { System.out.println("Count: " + i); } }}
MultiplasThreadspublic class MultiplasThreads { public static void main(String[] args) { System.out.println("The main thread of execution started"); RunCounter1 rct1 = new RunCounter1("First Thread"); RunCounter1 rct2 = new RunCounter1("Second Thread"); RunCounter1 rct3 = new RunCounter1("Third Thread"); }}class RunCounter1 implements Runnable { Thread myThread;
RunCounter1(String name) { myThread = new Thread(this, name); myThread.start(); } public void run() { for (int i = 1; i <= 5; i++) { System.out.println("Thread: " + myThread.getName() + " Count: " + i); } }}
O método start• Quando o método start é chamado a thread
não roda imediatamente.• Ela vai para o scheduler.
Estados• Novo - estado que uma thread fica no momento de sua instanciação, antes da
chamada do método start();• Executável - estado em que a thread fica disponível para ser executada e no
aguardo do escalonador de thread, esperando a sua vez de se executar;• Execução - Momento em que a thread está executando, está operando;• Espera/Bloqueio/Suspensão - esse estado pode ser dar por inúmeros
motivos. – Uma thread em sua execução pode se bloquear porque algum recurso ou
objeto não está disponível, por isso seu estado pode ficar bloqueado, até que esse recurso/objeto esteja disponível novamente assim seu estado torna-se executável, ou então, uma thread pode ficar suspensa porque o programador definiu um tempo de espera, assim que esse tempo expirar essa thread volta ao estado executável para continuar seus serviços;
• Inativo - a partir do momento em que o método run() foi concluído, a thread se tornará inativa, porém ainda existirá o objeto na memória, somente não como uma linha de execução, e não poderá novamente ser iniciada, ou seja, qualquer tentativa de chamada do método start() após a conclusão do métodos run(), uma exceção será lançada;
TesteRunnablepublic class TesteRunnable { public static void main(String[] args) { RunCounter rct = new RunCounter(); Thread th = new Thread(rct); th.start(); System.out.println("Thread já iniciada"); th.start(); }}
Métodos • run() - é o código que a thread executará.• start() - sinaliza à JVM que a thread pode ser executada, mas saiba
que essa execução não é garantida quando esse método é chamado, e isso pode depender da JVM.
• isAlive() - volta true se a thread está sendo executada e ainda não terminou.
• sleep() - suspende a execução da thread por um tempo determinado;• yield() - torna o estado de uma thread executável para que thread
com prioridades equivalentes possam ser processadas;• currentThread() - é um método estático da classe Thread que volta
qual a thread que está sendo executada.• getName() - volta o nome da Thread, você pode especificar o nome
de uma Thread com o método setName() ou na construção da mesma, pois existe os construtores sobrecarregados.
ExemploContadorpublic class ExemploContador { public static void main(String args[]) { ContadorThread c1 = new ContadorThread(); c1.setQtde(10); c1.setName("t001"); c1.start(); ContadorThread c2 = new ContadorThread(); c2.setQtde(15); c2.setName("t002"); c2.start(); }}
Cont...class ContadorThread extends Thread { private int qtde = 0; public void run() { for (int i = 0; i <= 100; i++) { if ((i % qtde) == 0) { System.out.println(Thread.currentThread().getName() + "> " + i); } try { sleep(50); } catch (InterruptedException ex) { } } }
public void setQtde(int value) { this.qtde = value; if (this.qtde == 0) this.qtde = 10; }}
yield• Uma chamada a yield (Thread.yield()) coloca
a thread de volta no estado de executável, permitindo que outras threads com a mesma prioridade (ou maior) possam executar.
• Esse método se propõe a isso. Mas não garante que o comportamento ocorra.
• Somente pára a execução se outra estiver pronta para executar.
sleep• Coloca a thread em espera. Após o período
de sleep acabar, a thread volta para o estado de executável: não entra imediamente em execução.
• Pára a execução em todos os casos.
wait• Usado em blocos sincronizados.• Pára a execução até ser notificado por outra
thread para retornar.
notify• Acorda uma thread que está na fila do
escalonador. • Se essa thread estiver pronta muda o seu
estado para executável.• notifyAll() acorda todas as threads em
estado de aguardando.• wait(), notify() e notifyAll() devem ser usados
em blocos sincronizados e são implementados em Object.
ClienteSocketimport java.io.*;import java.net.*;public class ClienteSocket { public void leDados() { try { Socket clientSock = new Socket("195.45.3.4", 11000); InputStream in = clientSock.getInputStream(); int len = 0; BufferedOutputStream outFile = new BufferedOutputStream( new FileOutputStream("response.txt")); byte buf[] = new byte[256]; while ((len = in.read(buf)) != -1) { outFile.write(buf, 0, len); } } catch (IOException e) { e.printStackTrace(); } }}
Block• A thread está bloqueada no método read.• Operações de IO colocar a thread
naturalmente em block.
Questões• Quais métodos são usados para executar
comunicação entre threads?
A. yield()
B. sleep(…)
C. notify()
D. wait()
Questões• Quais desses métodos estão definidos em
Object?
A. yield()
B. sleep(…)
C. run()
D. wait()
E. notify()
QuestaoThreadpublic class QuestaoThread { public static void main(String[] args) { CounterT ct = new CounterT(); ct.start(); System.out.println("The thread has been started"); }}class CounterT extends Thread { protected void run() { System.out.println("Hello"); }}Quais a saída?• The thread has been started. / Hello• Hello / The thread has been started.• Qualquer das anteriores• Erro na linha 9
Questões• Quais declarações são verdadeiras sobre o método
wait()? A. Uma thread chama wait() para parar
temporariamente a execução a execução de outras threads.
B. Quando uma thread executa wait(), ela pára sua execução temporariamente.
C. Uma chamada a wait() pára a execução da aplicação.
D. wait() pertence à classe Object.E. wait() pertence à classe Thread.
Veja o códigopublic class ThreadOrder { static int count = 0; public static void main(String[] args) { CounterO ct = new CounterO(); TrackerO trk1 = new TrackerO(ct, "thread one"); TrackerO trk2 = new TrackerO(ct, "thread two"); trk1.start(); trk2.start(); }}class TrackerO extends Thread { CounterO ct; String message; TrackerO(CounterO ct, String msg) { this.ct = ct; message = msg; } public void run() { System.out.println(message); }}class CounterO { private int count = 0;
public int nextCounter() { synchronized (this) { count++; return count; } }}
Questão• thread one / thread two• thread two / thread one• Às vezes a primeira alternativa, outras a
segunda.• Exceção na linha 8.
Questão• O que acontece quando uma thread tem o seguinte
bloco de código em seu método run? – sleep(500);
1. A execução pára e reinicia 500ms depois.
2. A execução pára e reinicia não antes que 500ms.
3. Erro de compilação. Você não pode chamar o método sleep.
4. Erro de compilação porque sleep não permite argumentos.
notify• Uma thread thr está aguardando entre outras para
ser executada. Como você pode usar notify() para tirar thr do estado de aguardando?
• Execute thr.notify() de um bloco de código sincronizado.
• Execute notify(thr) de um bloco de código sincronizado.
• Com notify você não pode especificar qual thread seria retirada do estado de aguardando.
Questões• Quais desses métodos garantem que uma
thread será colocada fora do estado de executando?
A. wait()
B. yield()
C. sleep(500)
D. kill()
E. notify()
Questão• Quais dos seguintes são construtores
válidos para Thread()?
A. Thread()
B. Thread(int millisec)
C. Thread(Runnable r)
D. Thread(Runnable r, String name)
E. Thread(int priority)
Questões• Quais desses métodos são definidos na
classe Thread?
A. yield()
B. sleep(…)
C. run()
D. wait()
E. notify()
Exemplos• Situações um pouco mais complexas.
TesteProdutopublic class TesteProduto { public static void main(String[] args) { Produto p = new Produto(5); Thread[] t = new Thread[15]; for (int i = 0; i < t.length; i++) { t[i] = new Thread(p); t[i].setName("Cliente: " + i); t[i].start(); } }}
public class Produto implements Runnable { private int estoque = 5;
public void run() { try { for (int i = 0; i < 2; i++) { efetuarPedido(); } } catch (Exception ex) { } }
Cont... public void efetuarPedido() { try { if (this.estoque > 0) { System.out.println("Pedido faturado para o cliente " + Thread.currentThread().getName()); Thread.sleep(250); this.estoque--; } else { System.out.println("Não tem estoque para o cliente " + Thread.currentThread().getName()); } } catch (Exception ex) { } }
public Produto(int value) { this.estoque = value; }}
Sincronização• Mude o método efetuarPedido para:
– public synchronized efetuarPedido
InteraThreadimport java.io.*; public class InteraThread { public static void main(String[] args) { try { Arquivo arq = new Arquivo(new File("saida.txt")); Thread[] a = new Thread[2]; for (int i=0; i < a.length; i++) { a[i] = new Thread(new Leitura(arq)); a[i].setName( ""+i); a[i].start(); } Thread b = new Thread( new Gravacao(arq) ); b.start(); b.join(); System.out.println("Processo finalizado..."); } catch (Exception ex) {} } }
Gravacaoclass Gravacao implements Runnable { Arquivo arq; public Gravacao(Arquivo value) { arq = value; } public void run() { arq.gravar(); } } class Leitura implements Runnable { Arquivo arq; public Leitura(Arquivo value) { arq = value; } public void run() { arq.ler(); } }
Arquivoclass Arquivo { File file; public Arquivo(File value) { file = value; } synchronized public void ler() { try { if (!file.exists()){ wait(); } System.out.print( "thread# "+Thread.currentThread().getName() + ">>> "); if (file.exists()){ FileInputStream fis = new FileInputStream(file); int in; while ((in=fis.read())!=-1) { System.out.print((char)in); } fis.close(); } } catch (Exception e) { e.printStackTrace(); } }
Cont... synchronized public void gravar() { try { if (!file.exists()){ FileOutputStream fos = new FileOutputStream(file); for (int i=0; i < 5; i++) { fos.write( ("linha "+i).getBytes() ); } fos.write("\n".getBytes()); fos.close(); System.out.print("Entrou no notify"); notify();
notity(); //ou notifyAll(); } } catch (Exception e) { e.printStackTrace(); } } }
• O código dispara 10 threads para leitura de um arquivo que nem foi criado.
• Dessa forma, todo mundo fica esperando até que esse arquivo seja criado.
• Uma vez criado o arquivo, a threads saem do estado wait e podem ler do disco.
TesteDeadLockpublic class TesteDeadLock { public static void main(String[] args) { Object obj1 = "objectA"; Object obj2 = "objectB"; DeadLock t1 = new DeadLock(obj1, obj2); DeadLock t2 = new DeadLock(obj2, obj1); t1.start(); t2.start(); System.out.println("Todas as threads foram iniciadas"); }}
DeadLockclass DeadLock extends Thread { private Object resourceA; private Object resourceB;
public DeadLock(Object a, Object b) { resourceA = a; resourceB = b; }
Cont... public void run() { while (true) { System.out.println("A thread " + Thread.currentThread().getName() + " está esperando pelo lock de " + resourceA); synchronized (resourceA) { System.out.println("A thread " + Thread.currentThread().getName() + " recebeu o lock de " + resourceA); System.out.println("A thread " + Thread.currentThread().getName() + " está esperando pelo lock de " + resourceB); synchronized (resourceB) { System.out.println("A thread " + Thread.currentThread().getName() + " recebeu o lock de " + resourceB); try { Thread.sleep(500); } catch (Exception e) { } } } } }}
Dead Lock• Dois ou mais processos estão esperando
por um evento.• O problema: os dois processos estão em
estado de waiting (aguardando).