79
UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO PROGRAMAÇÃO CONCORRENTE – 2015.1 Fábio M. Pereira ([email protected])

04 - Gerenciamento de Threads - II

Embed Size (px)

Citation preview

Page 1: 04 -  Gerenciamento de Threads - II

UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO

PROGRAMAÇÃO CONCORRENTE – 2015.1

Fábio M. Pereira

([email protected])

Page 2: 04 -  Gerenciamento de Threads - II

Roteiro

• Criando e executando uma daemon thread

• Processando exceções em uma thread

• Utilizando variáveis de tread locais

• Agrupando threads

• Processando exceções em um grupo de threads

• Criando threads através de uma fábrica

Page 3: 04 -  Gerenciamento de Threads - II
Page 4: 04 -  Gerenciamento de Threads - II

Criando e Executando Uma Daemon Thread

• Java tem um tipo especial de thread chamada de daemon thread

• Este tipo de thread tem prioridade muito baixa e normalmente só é executada quando nenhuma outra thread do mesmo programa está sendo executada

• Quando daemon threads são as únicas threads em execução em um programa, a JVM termina o programa terminando estas threads

• Com essas características, as daemon threads são normalmente utilizadas como provedores de serviços para threads normais (também chamadas de usuários) em execução no mesmo programa

Page 5: 04 -  Gerenciamento de Threads - II

Criando e Executando Uma Daemon Thread

• Elas geralmente têm um laço infinito que aguarda a solicitação de serviço ou executa as tarefas da thread

• Elas não podem fazer tarefas importantes porque não sabemos quando elas vão ter tempo de CPU e podem terminar a qualquer momento se não existem quaisquer outras threads em execução

• Um exemplo típico deste tipo de thread é o coletor de lixo Java

• No exemplo utilizaremos duas threads: uma thread do usuário que grava eventos em uma fila e uma daemon que limpa essa fila, removendo os eventos que foram gerados mais de 10 segundos atrás

Page 6: 04 -  Gerenciamento de Threads - II

Programa Exemplo

1. Crie a classe Event, esta classe só armazena informações sobre os eventos que o nosso programa irá trabalhar. Declare dois atributos particulares, um chamado date do tipo java.util.Date e outro chamado de event do tipo String. Gerar os métodos para escrever e ler seus valores.

2. Crie uma classe WriterTask e especifique que ela implementa a interface Runnable. public class WriterTask implements Runnable {

3. Declare a fila que armazena os eventos e implemente o construtor da classe, que inicializa esta fila. private Deque<Event> deque;

public WriterTask (Deque<Event> deque){

this.deque=deque;

}

Page 7: 04 -  Gerenciamento de Threads - II

Programa Exemplo

4. Implemente o método run() para esta tarefa. Este método deverá ter um laço com 100 iterações. Cada iteração deverá criar um novo evento (Event), salvá-lo na fila e dormir por um segundo. @Override

public void run() {

for (int i=1; i<100; i++) {

Event event=new Event();

event.setDate(new Date());

event.setEvent(String.format("A thread %s gerou

um evento",Thread.currentThread().getId()));

deque.addFirst(event);

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

Page 8: 04 -  Gerenciamento de Threads - II

Programa Exemplo

5. Crie a classe CleanerTask e especifique que ela estende a classe Thread. public class CleanerTask extends Thread {

6. Declare a fila que armazena os eventos e implemente o construtor da classe, que inicializa esta fila. No construtor, marque esta thread como um daemon através do método setDaemon(). private Deque<Event> deque;

public CleanerTask(Deque<Event> deque) {

this.deque = deque;

setDaemon(true);

}

Page 9: 04 -  Gerenciamento de Threads - II

Programa Exemplo

7. Implemente o método run(). Este método possui um laço infinito que pega a data atual e chama o método clean(). @Override

public void run() {

while (true) {

Date date = new Date();

clean(date);

}

}

Page 10: 04 -  Gerenciamento de Threads - II

Programa Exemplo

8. Implemente o método clean(). Ele pega o último evento e, se foi criado mais de 10 segundos atrás, o apaga e verifica o próximo evento. Se o evento é apagado, escreve uma mensagem do evento e o novo tamanho da fila, para acompanharmos a evolução. private void clean(Date date) {

long difference;

boolean delete;

if (deque.size()==0) {

return;

}

delete=false;

...

Page 11: 04 -  Gerenciamento de Threads - II

Programa Exemplo

8. Continuação. ...

do {

Event e = deque.getLast();

difference = date.getTime() –

e.getDate().getTime();

if (difference > 10000) {

System.out.printf("Cleaner: %s\n",e.getEvent());

deque.removeLast();

delete=true;

}

} while (difference > 10000);

if (delete){

System.out.printf("Cleaner: Tamanho da fila: %d\n",

deque.size());

}

}

Page 12: 04 -  Gerenciamento de Threads - II

Programa Exemplo

9. Agora implemente a classe principal. Crie o método main(). public class Main {

public static void main(String[] args) {

10. Crie a fila para armazenar os eventos usando a classe Deque. Deque<Event> deque=new ArrayDeque<Event>();

11. Crie e inicialize três threads WriterTask e uma CleanerTask. WriterTask writer=new WriterTask(deque);

for (int i=0; i<3; i++){

Thread thread=new Thread(writer);

thread.start();

}

CleanerTask cleaner=new CleanerTask(deque);

cleaner.start();

Page 13: 04 -  Gerenciamento de Threads - II

Programa Exemplo

12. Execute o programa e veja os resultados.

Page 14: 04 -  Gerenciamento de Threads - II

Funcionamento

• Se analisarmos a saída de uma execução do programa, poderemos ver como a fila começa a crescer até que ela tenha 30 eventos e, em seguida, o seu tamanho varia entre 27 e 30 eventos até o final da execução

• O programa começa com três threads WriterTask, cada thread escreve um evento e dorme durante um segundo

• Após os primeiros 10 segundos, temos 30 threads na fila

• Durante estes 10 segundos CleanerTask vem executando, enquanto as três threads WriterTask estavam dormindo, mas não excluiu qualquer evento, porque todos eles foram gerados menos de 10 segundos atrás

Page 15: 04 -  Gerenciamento de Threads - II

Funcionamento

• Durante o resto da execução, CleanerTask exclui três eventos a cada segundo e as três threads WriterTask

escrevem mais três, portanto, o tamanho da fila varia entre 27 e 30 eventos

• Podemos brincar com o tempo em que as threads WriterTask ficam dormindo: se usarmos um valor menor, vamos ver que CleanerTask tem menos tempo de CPU e o tamanho da fila vai aumentar porque CleanerTask não exclui qualquer evento

Page 16: 04 -  Gerenciamento de Threads - II

Criando e Executando Uma Daemon Thread

• Só podemos chamar o método setDaemon() antes de chamar o método start(), uma vez que a thread está sendo executada, não podemos modificar o seu estado daemon

• Podemos usar o método isDaemon() para verificar se uma thread é uma daemon thread (o método retorna true) ou uma thread do usuário (o método retorna false)

Page 17: 04 -  Gerenciamento de Threads - II
Page 18: 04 -  Gerenciamento de Threads - II

Processando Exceções em Uma Thread

• Existem dois tipos de exceções em Java:

– As exceções verificadas: essas exceções devem ser especificadas na cláusula throws de um método ou capturadas dentro dele, por exemplo, IOException ou ClassNotFoundException

– Exceções não verificadas: essas exceções não têm que ser especificadas ou capturadas, por exemplo, NumberFormatException

• Quando uma exceção verificada é lançada dentro do método run() de um objeto Thread, temos de capturá-la e tratá-la, porque o método run() não aceita uma cláusula throws

Page 19: 04 -  Gerenciamento de Threads - II

Processando Exceções em Uma Thread

• Quando uma exceção não verificada é lançada dentro do método run() de um objeto Thread, o comportamento padrão é escrever o rastreamento da pilha no console e sair do programa

• Felizmente, Java nos fornece um mecanismo para capturar e tratar as exceções não verificadas lançadas em um objeto Thread para evitar que o programa termine

• Vamos aprender este mecanismo usando um exemplo

Page 20: 04 -  Gerenciamento de Threads - II

Programa Exemplo

1. Primeiro, temos de implementar uma classe para tratar as exceções não verificadas. Esta classe deve implementar a interface UncaughtExceptionHandler

e implementar o método uncaughtException() declarado na interface. No nosso caso, chame essa classe de ExceptionHandler e faça o método para gravar informações sobre a Exception e a Thread

que a lançou. Veja o código a seguir.

Page 21: 04 -  Gerenciamento de Threads - II

Programa Exemplo

public class ExceptionHandler implements

UncaughtExceptionHandler {

public void uncaughtException(Thread t, Throwable e)

{

System.out.printf("Uma exceção foi capturada\n");

System.out.printf("Thread: %s\n",t.getId());

System.out.printf("Exception: %s: %s\n",

e.getClass().getName(),e.getMessage());

System.out.printf("Stack Trace: \n");

e.printStackTrace(System.out);

System.out.printf("Thread status: %s\n",

t.getState());

}

}

Page 22: 04 -  Gerenciamento de Threads - II

Programa Exemplo

2. Agora, implemente uma classe que gera uma exceção não verificada. Chame esta classe de Task, especifique que ela implementa a interface Runnable, implemente o método run(), e force a exceção, por exemplo, ao tentar converter um valor de sequência de caracteres em um valor int. public class Task implements Runnable {

@Override

public void run() {

int numero=Integer.parseInt("TTT");

}

}

Page 23: 04 -  Gerenciamento de Threads - II

Programa Exemplo

3. Agora implemente a classe principal do exemplo. Implemente uma classe chamada Main com o método main(). public class Main {

public static void main(String[] args) {

4. Crie um objeto do tipo Task e uma Thread para executá-lo. Defina o manipulador de exceção não verificada usando o método setUncaughtExceptionHandler() e inicie a execução da Thread. Task task=new Task();

Thread thread=new Thread(task);

thread.setUncaughtExceptionHandler(

new ExceptionHandler());

thread.start();

}

}

Page 24: 04 -  Gerenciamento de Threads - II

Programa Exemplo

5. Execute o exemplo e veja os resultados.

Page 25: 04 -  Gerenciamento de Threads - II

Funcionamento

• A exceção é lançada e capturado pelo manipulador que escreve no console as informações sobre a exceção e a Thread que a lançou

• Quando uma exceção é lançada em uma thread e não é capturada (tem que ser uma exceção não verificada), a JVM verifica se a thread tem um manipulador de exceção não capturada definido pelo método correspondente, se tiver, a JVM invoca este método com os objetos Thread e Exception como argumentos

• Se a thread não possui um manipulador de exceção não capturada, o JVM imprime o rastreamento da pilha no console e sai do programa

Page 26: 04 -  Gerenciamento de Threads - II

Processando Exceções em Uma Thread

• A classe Thread tem outro método relacionado com o processo de exceções, ele é o método estático setDefaultUncaughtExceptionHandler() que estabelece um manipulador de exceção para todos os objetos Thread da aplicação

Page 27: 04 -  Gerenciamento de Threads - II

Processando Exceções em Uma Thread

• Quando uma exceção não detectada é lançada em Thread, a JVM procura por três manipuladores possíveis para essa exceção:

– Primeiro, ele busca pelo manipulador de exceção não capturada dos objetos Thread como aprendemos neste exemplo

– Se o manipulador não existe, então a JVM busca pelo manipulador de exceção não capturada do ThreadGroup dos objetos Thread como veremos adiante

– Se esse método não existe, a JVM busca pelo manipulador de exceção não capturada padrão

– Se nenhum deles existe, a JVM imprime o rastreamento da pilha da exceção no console e sai do programa

Page 28: 04 -  Gerenciamento de Threads - II
Page 29: 04 -  Gerenciamento de Threads - II

Utilizando Variáveis de Thread Locais

• Um dos aspectos mais críticos de uma aplicação concorrente é o compartilhamento de dados

• Isto tem uma importância especial naqueles objetos que estendem a classe Thread ou implementam a interface Runnable

• Se você criar um objeto de uma classe que implementa a interface Runnable e, em seguida, iniciar vários objetos Thread usando o mesmo objeto Runnable, todos as threads compartilham os mesmos atributos

• Isto significa que, se você alterar um atributo em uma thread, todas as threads serão afetados por esta mudança

Page 30: 04 -  Gerenciamento de Threads - II

Utilizando Variáveis de Thread Locais

• Às vezes, podemos estar interessados em ter um atributo que não será compartilhado entre todas as threads que executam o mesmo objeto

• A API de Concorrência Java fornece um mecanismo limpo chamado de variáveis de threads locais com um desempenho muito bom

• Neste exemplo, iremos desenvolver um programa que tem o problema mostrado no início e outro programa que resolve esse problema usando o mecanismo de variáveis de threads locais

Page 31: 04 -  Gerenciamento de Threads - II

Programa Exemplo

1. Primeiro, vamos implementar um programa que tem o problema exposto anteriormente. Crie uma classe chamada UnsafeTask e especifique que ela implementa a interface Runnable. Declare um atributo java.util.Date privado. public class UnsafeTask implements

Runnable{

private Date startDate;

2. Implemente o método run() do objeto UnsafeTask. Este método irá inicializar o atributo startDate, escrever o seu valor no console, esperar por um período de tempo aleatório, e novamente escrever o valor do atributo startDate.

Page 32: 04 -  Gerenciamento de Threads - II

Programa Exemplo

@Override

public void run() {

startDate=new Date();

System.out.printf(“Iniciando a Thread: %s :

%s\n",Thread.currentThread().getId(),

startDate);

try {

TimeUnit.SECONDS.sleep((int)Math.rint(

Math.random()*10));

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.printf("Thread Finalizada: %s :

%s\n",Thread.currentThread().getId(),

startDate);

}

Page 33: 04 -  Gerenciamento de Threads - II

Programa Exemplo

3. Agora, vamos implementar a classe principal desta aplicação problemática. Crie uma classe chamada Main com um método main(). Este método irá criar um objeto da classe UnsafeTask e iniciar três threads usando esse objeto, dormindo por 2 segundos entre cada thread. public class Main {

public static void main(String[] args) {

UnsafeTask task=new UnsafeTask();

for (int i=0; i<3; i++){

Thread thread=new Thread(task);

thread.start();

try { TimeUnit.SECONDS.sleep(2);

} catch (InterruptedException e) {

e.printStackTrace();

}

} } }

Page 34: 04 -  Gerenciamento de Threads - II

Programa Exemplo

4. Na captura de tela seguinte, podemos ver os resultados da execução deste programa. Cada thread tem uma hora de início diferentes, mas, quando terminam, todas têm o mesmo valor no seu atributo startDate.

Page 35: 04 -  Gerenciamento de Threads - II

Programa Exemplo

5. Como mencionado anteriormente, iremos usar o mecanismo de variáveis de thread locais para resolver este problema.

6. Crie uma classe chamada SafeTask e especifique que ela implementa a interface Runnable. public class SafeTask implements Runnable {

7. Declare um objeto da classe ThreadLocal<Date>. Este objeto possuirá uma implementação implícita que inclui o método initialValue(). Este método irá retornar a data atual. private static ThreadLocal<Date> startDate=

new ThreadLocal<Date>() {

protected Date initialValue(){

return new Date();

}

};

Page 36: 04 -  Gerenciamento de Threads - II

Programa Exemplo

8. Implemente o método run(). Este possui a mesma funcionalidade do método run() de UnsafeClass, mas modifica a maneira como acessa o atributo startDate. @Override

public void run() {

System.out.printf("Iniciando Thread: %s :

%s\n",Thread.currentThread().getId(),

startDate.get());

try {

TimeUnit.SECONDS.sleep((int)Math.rint(

Math.random()*10));

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.printf("Thread Finalizada: %s :

%s\n",Thread.currentThread().getId(),

startDate.get());

}

Page 37: 04 -  Gerenciamento de Threads - II

Programa Exemplo

9. A classe principal deste exemplo é a mesma que a do exemplo inseguro, mudando o nome da classe Runnable.

10. Execute o exemplo e analise a diferença.

Page 38: 04 -  Gerenciamento de Threads - II

Funcionamento

• Agora, os três objetos Thread têm o seu próprio valor do atributo startDate

• As variáveis de thread locais armazenam um valor de um atributo para cada Thread que usa uma destas variáveis

• Podemos ler o valor usando o método get() e alterar o valor usando o método set()

• A primeira vez que acessamos o valor de uma variável de thread local, se ela não tem nenhum valor para o objeto Thread que está chamando, a variável de thread local chama o método initialValue() para atribuir um valor para essa Thread e retorna o valor inicial

Page 39: 04 -  Gerenciamento de Threads - II

Utilizando Variáveis de Thread Locais

• A classe de thread local também fornece o método remove(), que exclui o valor armazenado na variável de thread local para a thread que está chamando

• A API de Concorrência Java inclui a classe InheritableThreadLocal que fornece herança de valores para threads criadas a partir de outra thread

• Se uma thread A tem um valor em uma variável de thread local e criamos outra thread B, a thread B terá o mesmo valor que a thread A na variável de thread local

• Podemos sobrescrever o método childValue() que é chamado para inicializar o valor da thread filha na variável de thread local, ele recebe o valor da thread pai na variável de thread local como parâmetro

Page 40: 04 -  Gerenciamento de Threads - II
Page 41: 04 -  Gerenciamento de Threads - II

Agrupando Threads

• Uma funcionalidade interessante oferecida pela API de Concorrência Java é a capacidade de agrupar as threads

• Isto permite-nos tratar as threads de um grupo como uma unidade, proporcionando acesso aos objetos Thread que pertencem a um grupo e realizar uma operação com elas

• Por exemplo, temos algumas threads que fazem a mesma tarefa e queremos controlá-las, independentemente de quantas threads ainda estão em execução, o estado de cada uma irá interromper todas elas com uma única chamada

Page 42: 04 -  Gerenciamento de Threads - II

Agrupando Threads

• Java fornece a classe ThreadGroup para trabalhar com grupos de threads

• Um objeto ThreadGroup pode ser formado por objetos Thread e por outro objeto ThreadGroup, gerando uma estrutura de árvore de threads

• Neste exemplo, trabalharemos com objetos ThreadGroup desenvolvendo um exemplo simples: teremos 10 threads dormindo durante um período de tempo aleatório (simulando uma busca, por exemplo) e, quando um deles acabar, iremos interromper o resto

Page 43: 04 -  Gerenciamento de Threads - II

Programa Exemplo

1. Primeiro, crie uma classe chamada Result. Ele irá armazenar o nome da Thread que termina em primeiro lugar. Declare um atributo privado String chamado name e os métodos para ler e definir o valor.

2. Crie uma classe chamada SeachTask e especifique que ela implementa a interface Runnable. public class SearchTask implements Runnable {

3. Declare um atributo privado da classe Result e implemente o construtor da classe que inicializa este atributo. private Result result;

public SearchTask(Result result) {

this.result=result;

}

Page 44: 04 -  Gerenciamento de Threads - II

Programa Exemplo

4. Implemente o método run(). Ele vai chamar o método doTask() e esperar que ele termine ou por uma exceção InterruptedException. O método gravará mensagens para indicar o início, fim ou interrupção desta Thread. @Override

public void run() {

...

Page 45: 04 -  Gerenciamento de Threads - II

Programa Exemplo

@Override

public void run() {

String name=Thread.currentThread().getName();

System.out.printf("Thread %s:

Iniciada\n",name);

try {

doTask();

result.setName(name);

} catch (InterruptedException e) {

System.out.printf("Thread %s:

Interrompida\n",name);

return;

}

System.out.printf("Thread %s:

Terminada\n",name);

}

Page 46: 04 -  Gerenciamento de Threads - II

Programa Exemplo

5. Implemente o método doTask(). Ele irá criar um objeto Random para gerar um número aleatório e chamará o método sleep() com o número gerado. private void doTask() throws

InterruptedException {

Random random=new Random((new Date())

.getTime());

int value=(int)(random.nextDouble()*100);

System.out.printf("Thread %s: %d\n",

Thread.currentThread().getName(),

value);

TimeUnit.SECONDS.sleep(value);

}

Page 47: 04 -  Gerenciamento de Threads - II

Programa Exemplo

6. Agora crie a classe principal do exemplo e implemente o método main(). public class Main {

public static void main(String[] args) {

7. Primeiro, crie um objeto ThreadGroup e o chame de Searcher. ThreadGroup threadGroup = new

ThreadGroup("Searcher");

8. Então, crie um objeto SearchTask e um objeto Result. Result result=new Result();

SearchTask searchTask = new

SearchTask(result);

Page 48: 04 -  Gerenciamento de Threads - II

Programa Exemplo

9. Agora, crie 10 objetos Thread usando o objeto SearchTask. Quando você chamar o construtor da classe Thread, passe como primeiro argumento o objeto ThreadGroup. for (int i=0; i<10; i++) {

Thread thread=new Thread(threadGroup,

searchTask);

thread.start();

try { TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

Page 49: 04 -  Gerenciamento de Threads - II

Programa Exemplo

10. Imprima informação sobre o objeto ThreadGroup usando o método list(). System.out.printf("Número de Threads: %d\n",

threadGroup.activeCount());

System.out.printf("Informação sobre o Grupo

de Threads\n");

threadGroup.list();

Page 50: 04 -  Gerenciamento de Threads - II

Programa Exemplo

11. Use os métodos activeCount() e enumerate() para saber quantos objetos Thread estão associados ao objeto ThreadGroup e obter uma lista deles. Podemos usar esse método para obter, por exemplo, o estado de cada Thread. Thread[] threads=new

Thread[threadGroup.activeCount()];

threadGroup.enumerate(threads);

for (int i=0; i<threadGroup.activeCount();

i++) {

System.out.printf("Thread %s: %s\n",

threads[i].getName(),

threads[i].getState());

}

Page 51: 04 -  Gerenciamento de Threads - II

Programa Exemplo

12. Chame o método waitFinish(). Iremos implementá-lo adiante. Ele irá esperar até que uma das threads do objeto ThreadGroup termine. waitFinish(threadGroup);

13. Interrompa o restante das threads do grupo usando o método interrupt(). threadGroup.interrupt();

Page 52: 04 -  Gerenciamento de Threads - II

Programa Exemplo

14. Implemente o método waitFinish(). Ele irá usar o método activeCount() para controlar o final de uma das threads. private static void waitFinish(ThreadGroup

threadGroup) {

while (threadGroup.activeCount()>9) {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

Page 53: 04 -  Gerenciamento de Threads - II

Programa Exemplo

15. Execute o exemplo e veja o resultado.

Page 54: 04 -  Gerenciamento de Threads - II

Programa Exemplo

15. Execute o exemplo e veja o resultado.

Page 55: 04 -  Gerenciamento de Threads - II

Programa Exemplo

15. Execute o exemplo e veja o resultado.

Page 56: 04 -  Gerenciamento de Threads - II

Funcionamento

• Na captura de tela, podemos ver a saída do método list() e a saída gerada quando escrevemos o estado de cada objeto Thread

• A classe ThreadGroup armazena os objetos Thread e outros objetos ThreadGroup associados a ela, de modo que ela pode acessar todas as suas informações (estado, por exemplo) e executar operações sobre todos os seus membros (interromper, por exemplo)

• A classe ThreadGroup possiu mais métodos, verifique a documentação da API para ter uma explicação completa de todos esses métodos

Page 57: 04 -  Gerenciamento de Threads - II
Page 58: 04 -  Gerenciamento de Threads - II

Processando Exceções em Um Grupo de Threads

• Um aspecto muito importante em toda linguagem de programação é o mecanismo que fornece gerenciamento de situações de erro na aplicação

• A linguagem Java, como quase todas as linguagens de programação modernas, implementa um mecanismo baseado em exceção para gerir situações de erro

• Ela fornece uma série de classes para representar erros diferentes

• Essas exceções são lançadas pelas classes Java quando uma situação de erro é detectada

• Podemos usar essas exceções, ou implementar nossas próprias exceções, para gerenciar os erros produzidos em nossas classes

Page 59: 04 -  Gerenciamento de Threads - II

Processando Exceções em Um Grupo de Threads

• Java também fornece um mecanismo para capturar e processar essas exceções

• Há exceções que devem ser capturados ou re-lançadas usando a cláusula throws de um método, essas exceções são chamados de exceções verificadas

• Há exceções que não têm de ser especificadas ou capturados, estas são as exceções não verificadas

• No tópico, Controlando a Interrupção de uma Thread, vimos como usar um método genérico para processar todas as exceções não verificadas que são lançadas em um objeto Thread

• Outra possibilidade é estabelecer um método que captura todas as exceções não capturadas, lançadas por qualquer Thread da classe ThreadGroup

Page 60: 04 -  Gerenciamento de Threads - II

Programa Exemplo

1. Em primeiro lugar, temos de estender a classe ThreadGroup criando uma classe chamada MyThreadGroup. Temos de declarar um construtor com um parâmetro, porque a classe ThreadGroup não tem um construtor sem ele. public class MyThreadGroup extends

ThreadGroup {

public MyThreadGroup(String name) {

super(name);

}

Page 61: 04 -  Gerenciamento de Threads - II

Programa Exemplo

2. Substitua o método uncaughtException(). Este método é chamado quando uma exceção é lançada em uma das threads da classe ThreadGroup. Neste caso, este método irá escrever no console informações sobre a exceção e da Thread que a lança, e interromper o restante das threads na classe ThreadGroup. @Override

public void uncaughtException(Thread t,

Throwable e) {

System.out.printf("A thread %s lançou uma

Exceção\n",t.getId());

e.printStackTrace(System.out);

System.out.printf("Terminando o restante das

Threads\n");

interrupt();

}

Page 62: 04 -  Gerenciamento de Threads - II

Programa Exemplo

3. Crie uma classe chamada Task e especifique que ele implementa a interface Runnable. public class Task implements Runnable {

4. Implemente o método run(). Neste caso, vamos provocar uma exceção AritmethicException. Para isso, vamos dividir 1.000 entre números aleatórios até que o gerador random gere um zero e a exceção seja lançada. @Override

public void run() {

...

Page 63: 04 -  Gerenciamento de Threads - II

Programa Exemplo

@Override

public void run() {

int result;

Random random=new Random(Thread.currentThread()

.getId());

while (true) {

result=1000/((int)(random.nextDouble()

*1000));

System.out.printf("%s : %d\n", Thread

.currentThread().getId(),result);

if (Thread.currentThread().isInterrupted()) {

System.out.printf("%d : Interrompida\n",Thread.

currentThread().getId());

return;

}

}

}

Page 64: 04 -  Gerenciamento de Threads - II

Programa Exemplo

5. Agora, vamos implementar a classe principal do exemplo e o método main(). public class Main {

public static void main(String[] args) {

6. Crie um objeto da classe MyThreadGroup. MyThreadGroup threadGroup=new

MyThreadGroup("MyThreadGroup");

7. Crie um objeto da classe Task. Task task=new Task();

Page 65: 04 -  Gerenciamento de Threads - II

Programa Exemplo

8. Crie dois objetos Thread com esta Task e inicie-os. for (int i=0; i<2; i++){

Thread t=new Thread(threadGroup,task);

t.start();

}

9. Execute o exemplo e veja os resultados.

Page 66: 04 -  Gerenciamento de Threads - II

Funcionamento

• Quando executamos o exemplo, vemos como um dos objetos Thread lança a exceção e o outro é interrompido

• Quando uma exceção não detectada é lançada em Thread, a JVM procura três possíveis manipuladores para essa exceção:

– Primeiro, ela busca pelo manipulador de exceção não capturada da thread, como foi explicado em tópico anterior

– Se o manipulador não existe, então a JVM busca pelo manipulador de exceção não capturada da classe ThreadGroup da thread, como vimos neste exemplo

– Se esse método não existe, a JVM busca pelo manipulador de exceção não capturada padrão

• Se nenhum dos manipuladores existir, a JVM escreve o rastreamento da pilha de exceção no console e sai do programa

Page 67: 04 -  Gerenciamento de Threads - II
Page 68: 04 -  Gerenciamento de Threads - II

Criando Threads Através de Uma Fábrica

• O padrão fábrica de objetos é um dos design patterns mais utilizados no mundo da programação orientada a objetos

• É um padrão criacional e seu objetivo é desenvolver um objeto cuja missão será a criação de outros objetos de uma ou de várias classes

• Então, quando nós queremos criar um objeto de uma dessas classes, usamos a fábrica em vez de usar o operador new

• Com esta fábrica, nós centralizamos a criação de objetos com algumas vantagens: – É fácil alterar a classe dos objetos criados ou a forma como criar esses

objetos

– É fácil limitar a criação de objetos para recursos limitados, por exemplo, nós só podemos ter n objetos de um tipo

– É fácil gerar dados estatísticos sobre a criação dos objetos

Page 69: 04 -  Gerenciamento de Threads - II

Criando Threads Através de Uma Fábrica

• Java fornece uma interface, a interface ThreadFactory

para implementar uma fábrica de objetos Thread

• Alguns utilitários avançados da API de concorrência Java usam fábricas de threads para criar threads

• Neste exemplo, vamos implementar uma interface ThreadFactory para criar objetos Thread com um nome personalizado, enquanto iremos salvar as estatísticas dos objetos Thread criados

Page 70: 04 -  Gerenciamento de Threads - II

Programa Exemplo

1. Crie uma classe chamada MyThreadFactory e especifique que ele implementa a interface ThreadFactory. public class MyThreadFactory implements

ThreadFactory

2. Declare três atributos: um número inteiro chamado de counter, que vamos usar para armazenar o número do objeto Thread criado, uma String denominada name

com o nome base de cada Thread criada, e uma lista (List) de objetos String chamada stats para salvar os dados estatísticos sobre os objetos Thread criados. Devemos também implementar o construtor da classe que inicializa esses atributos.

Page 71: 04 -  Gerenciamento de Threads - II

Programa Exemplo

private int counter;

private String name;

private List<String> stats;

public MyThreadFactory(String name){

counter=0;

this.name=name;

stats=new ArrayList<String>();

}

Page 72: 04 -  Gerenciamento de Threads - II

Programa Exemplo

3. Implementar o método newThread(). Este método irá receber uma interface Runnable e retorna um objeto Thread para esta interface Runnable. No nosso caso, nós geramos o nome do objeto Thread, criamos o novo objeto Thread, e salvamos as estatísticas. @Override

public Thread newThread(Runnable r) {

Thread t=new Thread(r,name+"-Thread_"

+counter);

counter++;

stats.add(String.format("Thread %d criada

com o nome %s em %s\n",t.getId(),

t.getName(),new Date()));

return t;

}

Page 73: 04 -  Gerenciamento de Threads - II

Programa Exemplo

4. Implemente o método getStatistics() que retorna um objeto String com os dados estatísticos de todos os objetos Thread criados. public String getStats(){

StringBuffer buffer=new StringBuffer();

Iterator<String> it = stats.iterator();

while (it.hasNext()) {

buffer.append(it.next());

}

return buffer.toString();

}

Page 74: 04 -  Gerenciamento de Threads - II

Programa Exemplo

5. Crie uma classe chamada Task e especifique que ela implementa a interface Runnable. Neste exemplo, essas tarefas não vão fazer nada além que dormir por um segundo. public class Task implements Runnable {

@Override

public void run() {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

Page 75: 04 -  Gerenciamento de Threads - II

Programa Exemplo

6. Crie a classe principal do exemplo e implemente o método main(). public class Main {

public static void main(String[] args) {

7. Crie um objeto MyThreadFactory e um objeto Task. MyThreadFactory factory=new

MyThreadFactory("MyThreadFactory");

Task task=new Task();

8. Crie 10 objetos Thread usando o objeto MyThreadFactory e os inicie. Thread thread;

System.out.printf("Iniciando as Threads\n");

for (int i=0; i<10; i++){

thread=factory.newThread(task);

thread.start();

}

Page 76: 04 -  Gerenciamento de Threads - II

Programa Exemplo

9. Escreva no console as estatísticas da fabrica de threads. System.out.printf("Estatísticas:\n");

System.out.printf("%s\n",factory.getStats());

10. Execute o exemplo e veja os resultados.

Page 77: 04 -  Gerenciamento de Threads - II

Funcionamento

• A interface ThreadFactory tem apenas um método chamado newThread

• Ele recebe um objeto Runnable como parâmetro e retorna um objeto Thread

• Quando implementamos uma interface ThreadFactory, temos que implementar essa interface e substituir esse método

• A maioria das ThreadFactory básicas, têm apenas uma única linha: return new Thread(r);

Page 78: 04 -  Gerenciamento de Threads - II

Funcionamento

• Podemos melhorar esta aplicação, adicionando algumas variantes: – Criando threads personalizadas, como no exemplo, usando um

formato especial para o nome ou até mesmo criando nossa própria classe Thread que herda da classe Thread Java

– Salvando estatísticas de criação de threads, como mostrado no exemplo anterior

– Limitando o número de threads criadas

– Validando a criação das threads

– E mais o que possamos imaginar

• O uso do padrão de design de fábrica é uma boa prática de programação, mas, se implementarmos uma interface ThreadFactory para centralizar a criação de threads, temos que rever o código para garantir que todas as threads são criadas usando essa fábrica

Page 79: 04 -  Gerenciamento de Threads - II

UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO

PROGRAMAÇÃO CONCORRENTE – 2015.1

Fábio M. Pereira

([email protected])