Coleções, Genéricos, Threads
Marco 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.
TesteColecaoComGenericos
package 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 parametrizados
1.
2.
3.
4.
Declaração do tipo parametrizado da coleção.
Declaração do tipo parametrizado do iterator.
Loop normal (while).
Não precisa mais de conversão, o compilador já
sabe que o objeto é do tipo Pessoa. Nem mesmo
aceitaria outro tipo de classe.
TesteColecaoComGenericos
package 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
•
•
1.
2.
Melhoramento do for tradicional.
Não é mais necessário utilizar o iterator.
Para cada Pessoa dentro da lista...
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.
CaixaDeObjeto
package 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.
TesteCaixaDeObjeto
package 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.
TesteCaixaDeObjeto
package 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.
CaixaDeObjeto
package 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.
TesteCaixaDeObjeto
package 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());
}
}
TesteCaixaDeObjeto
1. 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.
TesteCaixaDeObjeto
package 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.
CaixaDeObjeto
package 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());
}
}
TesteCaixaDeObjetoInspecao
package 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"));
}
}
Memory
import 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.
Execucao
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.currentThread().getName() + " executando");
}
}
}
class Proc implements Runnable {
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " executando");
}
}
}
TesteThread
public 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);
}
}
}
TesteThread
public 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);
}
}
}
MultiplasThreads
public 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;
TesteRunnable
public 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.
ExemploContador
public 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.
ClienteSocket
import 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()
QuestaoThread
public 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ódigo
public 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.
TesteProduto
public 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
InteraThread
import 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) {}
}
}
Gravacao
class 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();
}
}
Arquivo
class 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.
TesteDeadLock
public 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");
}
}
DeadLock
class 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).
Download

public class