1. UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA
CURSO DE CIÊNCIA DA COMPUTAÇÃO
PROGRAMAÇÃO CONCORRENTE – 2015.1
Fábio M. Pereira
(fabio.mpereira@uesb.edu.br)
2. 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
3.
4. 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
5. 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
6. 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;
}
7. 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();
}
}
}
8. 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);
}
9. 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);
}
}
10. 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;
...
11. Programa Exemplo
8. Continuação.
...
do {
Event e = deque.getLast();
difference = date.getTime() –
e.getDate().getTime();
if (difference > 10000) {
System.out.printf("Cleaner: %sn",e.getEvent());
deque.removeLast();
delete=true;
}
} while (difference > 10000);
if (delete){
System.out.printf("Cleaner: Tamanho da fila: %dn",
deque.size());
}
}
12. 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();
14. 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
15. 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
16. 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)
17.
18. 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
19. 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
20. 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.
21. Programa Exemplo
public class ExceptionHandler implements
UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e)
{
System.out.printf("Uma exceção foi capturadan");
System.out.printf("Thread: %sn",t.getId());
System.out.printf("Exception: %s: %sn",
e.getClass().getName(),e.getMessage());
System.out.printf("Stack Trace: n");
e.printStackTrace(System.out);
System.out.printf("Thread status: %sn",
t.getState());
}
}
22. 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");
}
}
23. 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();
}
}
25. 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
26. 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
27. 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
28.
29. 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
30. 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
31. 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.
33. 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();
}
} } }
34. 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.
35. 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();
}
};
36. 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 :
%sn",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 :
%sn",Thread.currentThread().getId(),
startDate.get());
}
37. 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.
38. 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
39. 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
40.
41. 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
42. 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
43. 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;
}
44. 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() {
...
46. 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: %dn",
Thread.currentThread().getName(),
value);
TimeUnit.SECONDS.sleep(value);
}
47. 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);
48. 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();
}
}
49. Programa Exemplo
10. Imprima informação sobre o objeto ThreadGroup
usando o método list().
System.out.printf("Número de Threads: %dn",
threadGroup.activeCount());
System.out.printf("Informação sobre o Grupo
de Threadsn");
threadGroup.list();
50. 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: %sn",
threads[i].getName(),
threads[i].getState());
}
51. 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();
52. 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();
}
}
}
56. 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
57.
58. 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
59. 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
60. 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);
}
61. 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çãon",t.getId());
e.printStackTrace(System.out);
System.out.printf("Terminando o restante das
Threadsn");
interrupt();
}
62. 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() {
...
63. 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 : %dn", Thread
.currentThread().getId(),result);
if (Thread.currentThread().isInterrupted()) {
System.out.printf("%d : Interrompidan",Thread.
currentThread().getId());
return;
}
}
}
64. 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();
65. 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.
66. 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
67.
68. 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
69. 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
70. 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.
71. 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>();
}
72. 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 %sn",t.getId(),
t.getName(),new Date()));
return t;
}
73. 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();
}
74. 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();
}
}
}
75. 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 Threadsn");
for (int i=0; i<10; i++){
thread=factory.newThread(task);
thread.start();
}
76. Programa Exemplo
9. Escreva no console as estatísticas da fabrica de threads.
System.out.printf("Estatísticas:n");
System.out.printf("%sn",factory.getStats());
10. Execute o exemplo e veja os resultados.
77. 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);
78. 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
79. UNIVERSIDADE ESTADUAL DO SUDOESTE DA BAHIA
CURSO DE CIÊNCIA DA COMPUTAÇÃO
PROGRAMAÇÃO CONCORRENTE – 2015.1
Fábio M. Pereira
(fabio.mpereira@uesb.edu.br)