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)
Roteiro
• Criando e executando uma thread
• Recuperando e atribuindo informações de uma thread
• Interrompendo uma thread
• Controlando a interrupção de uma thread
• Adormecendo e retomando uma thread
• Aguardando pela finalização de uma thread
Criando e Executando Uma Thread
• Vamos aprender como criar e executar uma thread em uma
aplicação Java
• Tal como acontece com todos os elementos da linguagem
Java, threads são objetos
• Temos duas maneiras de criar uma thread em Java:
– Estendendo a classe Thread e substituindo o método run()
– A construção de uma classe que implementa a interface Runnable e,
em seguida, criar um objeto da classe Thread passando o objeto que
implementa a interface Runnable como um parâmetro
• Vamos usar a segunda abordagem para criar um programa
simples que cria e executa 10 threads
• Cada thread calcula e imprime a tabela de multiplicação de
um número entre um e 10
Criando o Programa
1. Crie um novo projeto Java
2. Crie uma classe chamada Calculadora que
implemente a interface Runnable
public class Calculadora implements Runnable
{
3. Declare um atributo private int chamado numero e
implemente o construtor da classe inicializando o seu
valor
private int numero;
public Calculadora(int numero) {
this.numero=numero;
}
Criando o Programa
4. Implemente o método run(), este método irá executar as
instruções da thread que criamos, assim, este método irá
calcular a tabela de multiplicação para o número
@Override
public void run() {
for (int i=1; i<=10; i++){
System.out.printf("%s: %d * %d = %dn",
Thread.currentThread().getName(),
numero,i,i*numero);
}
}
5. Agora implemente a classe principal da aplicação, criando
uma classe Main que contém o método main()
public class Main {
public static void main(String[] args) {
Criando o Programa
6. Dentro do método main() crie um laço for para 10
iterações, dentro do laço crie um objeto da classe
Calculadora, um objeto da classe Thread, passe o
objeto Calculadora como parâmetro, e chame o
método start() do objeto thread
for (int i=1; i<=10; i++){
Calculadora calc=new Calculadora(i);
Thread thread=new Thread(calc);
thread.start();
}
Funcionamento...
• Execute o programa e veja como as diferentes threads
trabalham em paralelo
Funcionamento...
• Cada programa Java tem pelo menos uma thread de
execução
• Quando executamos o programa, a JVM executa essa
thread de execução que chama o método main() do
programa
• Quando chamamos o método start() de um objeto
Thread, estamos criando uma outra thread de execução
• Nosso programa terá tantas threads de execução quanto
chamadas do método start()
• Um programa Java termina quando todas as suas threads
terminarem (mais especificamente, quando todas as suas
threads não-daemon terminarem)
Funcionamento...
• Se a thread inicial (aquele que executa o método
main()) termina, o restante das threads irá continuar
com a sua execução até que terminem
• Se uma das threads chamar a instrução System.Exit()
para terminar a execução do programa, todas as threads
irão terminar a sua execução
• Criar um objeto da classe Thread não cria uma nova
thread de execução
• Além disso, chamar o método run() de uma classe que
implementa a interface Runnable não cria uma nova
thread de execução
• Apenas chamar o método start() cria uma nova
thread de execução
Funcionamento...
• Como mencionado no início, há uma outra maneira de
criar uma nova thread de execução:
– Podemos implementar uma classe que estende a classe Thread
e substituir o método run() da classe
– Em seguida, podemos criar um objeto dessa classe e chamar o
método start() para ter uma nova thread de execução
Recuperando e Atribuindo Informações
de Uma Thread
• A classe Thread salva alguns atributos de informações
que podem nos ajudar a identificar uma thread, saber o
seu status, ou controlar a sua prioridade
• Estes atributos são:
– ID: armazena um identificador único para cada Thread
– Nome: armazena o nome da Thread
– Prioridade: armazena a prioridade dos objetos Thread. Threads
podem ter uma prioridade entre um e dez, onde um é a
prioridade mais baixa e dez é a mais alta. Não é recomendado
alterar a prioridade das threads, mas é uma possibilidade que
podemos usar se quisermos
– Status: armazena o status da Thread. Em Java, Thread pode
estar em um desses seis estados: new, runnable, blocked,
waiting, time waiting, ou terminated
Recuperando e Atribuindo Informações
de Uma Thread
• Neste exemplo, vamos desenvolver um programa que
estabelece o nome e prioridade para 10 threads e, em
seguida, mostra informações sobre o seu estado até que
elas terminem
• As threads irão calcular a tabela de multiplicação de um
número
Criando o Programa
1. Repita os passos de 1 a 5 do exemplo anterior para
criação da classe Calculadora e do programa principal
6. Agora crie um array de 10 elementos Thread e um array
com 10 elementos Thread.State para armazenar as
threads que iremos executar e os seus status
Thread threads[]=new Thread[10];
Thread.State status[]=new Thread.State[10];
Criando o Programa
7. Crie 10 objetos da classe Calculadora, cada um inicializado
com um número diferente, e 10 threads para executá-los,
atribua valor máximo de prioridade a cinco delas e valor de
prioridade mínimo às outras cinco
for (int i=0; i<10; i++){
threads[i]=new Thread(new Calculadora(i+1));
if ((i%2)==0){
threads[i].
setPriority(Thread.MAX_PRIORITY);
} else {
threads[i].
setPriority(Thread.MIN_PRIORITY);
}
threads[i].setName("Thread "+i);
}
Criando o Programa
8. Crie um objeto PrintWriter para escrever em um arquivo a evolução
do status das threads
try (
FileWriter file = new FileWriter(
".log.txt"); // "./log.txt" no Linux!
PrintWriter pw = new PrintWriter(file);) {
// O resto do programa (itens 9 a 11) vai aqui!
// ...
} catch (IOException e) { e.printStackTrace(); }
9. Escreva neste arquivo o status das 10 threads
for (int i=0; i<10; i++){
pw.println("Main : Status da Thread "+i+" : “
+ threads[i].getState());
status[i]=threads[i].getState();
}
10. Inicie a execução das 10 threads
for (int i=0; i<10; i++){
threads[i].start();
}
Criando o Programa
11. Até que as 10 threads terminem, iremos checar os seus status, se
detectarmos uma mudança no status da thread, iremos escrevê-la no
arquivo
boolean finish=false;
while (!finish) {
for (int i=0; i<10; i++){
if (threads[i].getState()!=status[i]) {
writeThreadInfo(pw, threads[i],status[i]);
status[i]=threads[i].getState();
}
}
finish=true;
for (int i=0; i<10; i++){
finish=finish &&
(threads[i].getState()==
Thread.State.TERMINATED);
}
}
pw.close();
Criando o Programa
12. Implemente o método writeThreadInfo() que escreve
a ID, nome, prioridade, status anterior e novo status da
thread
private static void writeThreadInfo(PrintWriter
pw, Thread thread, Thread.State state) {
pw.printf("Main : Id %d - %sn",thread.getId(),
thread.getName());
pw.printf("Main : Prioridade: %dn",
thread.getPriority());
pw.printf("Main : Estado anterior: %sn",
state);
pw.printf("Main : Novo Estado: %sn",
thread.getState());
pw.printf("Main : **********************" +
"**************n");
}
Criando o Programa
• Execute o exemplo e abra o arquivo log.txt para ver a
evolução das 10 threads
Funcionamento...
• O programa mostra no console as tabuadas calculadas pelas
threads e a evolução do estado das diferentes threads no
arquivo log.txt, desta forma podemos visualizar melhor a
evolução das threads
• A classe Thread tem atributos para armazenar todas as
informações de uma thread
• A JVM utiliza a prioridade das threads para selecionar aquela
que usa a CPU em cada momento e atualiza o status de cada
thread de acordo com a sua situação
• Se não especificarmos um nome para uma thread, a JVM
automaticamente atribui a ela um nome com o formato,
Thread-XX, onde XX é um número
• Não podemos modificar a ID ou o status de uma thread, a
classe Thread não implementa os métodos setId() e
setStatus() para permitir a sua alteração
Funcionamento...
• Também podemos acessar os atributos de uma thread a
partir da implementação da interface Runnable,
podemos usar o método estático CurrentThread() da
classe Thread para acessar o objeto Thread que está
executando o objeto Runnable
• Devemos levar em conta que o método setPriority()
pode lançar uma exceção IllegalArgumentException
se tentarmos estabelecer uma prioridade que não está
entre 1 e 10
Interrompendo Uma Thread
• Um programa Java com mais de uma thread de execução
só termina quando a execução de todas as suas threads
e, mais especificamente, quando todas as suas threads
não-daemon terminam a sua execução ou quando uma
das threads usam o método System.exit()
• Às vezes, vamos precisar terminar uma thread, porque
queremos encerrar um programa, ou quando um usuário
do programa quer cancelar as tarefas que um objeto
Thread está fazendo
• Java fornece um mecanismo de interrupção para indicar a
uma thread que queremos terminá-la
Interrompendo Uma Thread
• Uma peculiaridade deste mecanismo é que a Thread
tem que verificar se ela foi interrompida ou não, e pode
decidir se ela responde ao pedido de finalização ou não
• A Thread pode ignorá-lo e continuar com a sua execução
Programa Exemplo
1. Crie uma classe chamada PrimeGenerator que estende a classe
Thread
public class PrimeGenerator extends Thread{
2. Sobrescreva o método run() incluindo um laço que irá rodar
indefinidamente. Neste laço iremos processar números
consecutivos iniciando por um. Para cada número iremos calcular
se ele é primo e, neste caso, iremos imprimi-lo no console.
@Override
public void run() {
long number=1L;
while (true) {
if (isPrime(number)) {
System.out.printf("O numero %d eh Primon",
number);
}
...
Programa Exemplo
3. Após processar um número, verifique se a thread foi
interrompida através do método isInterrupted(). Se
o método retornar true, escreveremos uma mensagem
e terminaremos a execução da thread.
if (isInterrupted()) {
System.out.printf("O Gerador de
Primos foi interrompido!n");
return;
}
number++;
}
}
Programa Exemplo
4. Implemente o método isPrime(). Ele irá retornar um valor
boolean indicando se o número recebido como parâmetro
é um número primo (true) ou não (false).
private boolean isPrime(long number) {
if (number <=2) {
return true;
}
for (long i=2; i<number; i++){
if ((number % i)==0) {
return false;
}
}
return true;
}
Programa Exemplo
5. Agora implemente a classe principal do exemplo (Main),
implementando o método main().
public class Main {
public static void main(String[] args) {
6. Crie e inicie um objeto da classe PrimeGenerator.
Thread task=new PrimeGenerator();
task.start();
7. Espere por 5 segundos e interrompa a thread
PrimeGenerator.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
task.interrupt();
8. Rode o programa e veja os resultados.
Funcionamento...
Funcionamento...
• A classe Thread tem um atributo que armazena um valor
booleano que indica se a thread foi interrompida ou não
• Quando chamamos o método interrupt() de uma thread,
definimos esse atributo para true
• O método isInterrupted() retorna apenas o valor do
atributo
• A classe Thread tem outro método para verificar se foi
interrompida ou não, é o método estático, interrupted(),
que verifica se a thread atual em execução foi interrompida
ou não
– O método interrupted(), diferentemente de isInterrupted(),
modifica o valor do atributo interrupted para false
– Por se tratar de um método estático, a sua utilização não é
recomendada
Controlando a Interrupção de Uma
Thread
• O mecanismo mostrado no exemplo anterior, pode ser
utilizado se a thread que pode ser interrompida é
simples, mas se a thread implementa um algoritmo
complexo dividido em alguns métodos, ou se tem
métodos com chamadas recursivas, podemos usar um
mecanismo melhor para controlar a interrupção da
thread
• Java fornece a exceção InterruptedException com
esta finalidade, podemos lançar essa exceção quando
detectarmos a interrupção da thread e capturá-la no
método run()
Controlando a Interrupção de Uma
Thread
• Neste exemplo, vamos implementar uma Thread que
procura arquivos com um determinado nome em uma
pasta e em todas as suas subpastas para mostrar como
usar a exceção InterruptedException para controlar a
interrupção de uma thread
Programa Exemplo
1. Crie uma classe chamada FileSearch e especifique que ela
implementa a interface Runnable.
public class FileSearch implements Runnable {
2. Declare dois atributos privados, um para o nome do arquivo
que será procurado e outro para a pasta inicial. Implemente
o construtor dessa classe, que inicializa esses atributos.
private String initPath;
private String fileName;
public FileSearch(String initPath, String fileName) {
this.initPath = initPath;
this.fileName = fileName;
}
Programa Exemplo
3. Implemente o método run() da classe FileSearch. Ele
verifica se o atributo initPath é uma pasta e, se for, chama
o método processDirectory(). Este método pode lançar
uma exceção InterruptedException, devendo capturá-la.
@Override
public void run() {
File file = new File(initPath);
if (file.isDirectory()) {
try {
directoryProcess(file);
} catch (InterruptedException e) {
System.out.printf("%s: A busca foi interrompida",
Thread.currentThread().getName());
}
}
}
Programa Exemplo
4. Implemente o método directoryProcess(). Este
método irá obter e processar os arquivos e subpastas da
pasta. Para cada subpasta, o método irá fazer uma
chamada recursiva passando a pasta como parâmetro.
Para cada arquivo, o método irá chamar o métodos
fileProcess(). Após processar todos os arquivos e
pastas, o método verifica se a Thread foi interrompida
e, neste caso, lança uma exceção
InterruptedException.
Programa Exemplo
private void directoryProcess(File file) throws
InterruptedException {
System.out.printf("Processando %s ...n",
file.getName());
File list[] = file.listFiles();
if (list != null) {
for (int i = 0; i < list.length; i++) {
if (list[i].isDirectory()) {
directoryProcess(list[i]);
} else {
fileProcess(list[i]);
}
}
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
Programa Exemplo
5. Implemente o método processFile(). Este método irá
comparar o nome do arquivo que está sendo processado com o
nome que estamos buscando. Se forem iguais, iremos escrever
uma mensagem no console. Após esta comparação, a Thread irá
verificar se foi interrompida e, neste caso, irá lançar uma exceção
InterruptedException.
private void fileProcess(File file) throws
InterruptedException {
if (file.getName().equals(fileName)) {
System.out.printf("%s : %s !!!n",
Thread.currentThread().getName(),
file.getAbsolutePath());
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
Programa Exemplo
6. Agora vamos implementar a classe principal do
exemplo. Implemente a classe Main que contém o
método main().
public class Main {
public static void main(String[] args) {
7. Crie e inicialize um objeto da classe FileSearch e uma
Thread para executar a sua tarefa. Então inicie a
execução da Thread.
FileSearch searcher=new
FileSearch("C:","autoexec.bat");
// Modificar! Principalmente no Linux…
Thread thread=new Thread(searcher);
thread.start();
Programa Exemplo
8. Aguarde por 10 segundos e interrompa a Thread.
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
9. Execute o programa e veja os resultados.
Funcionamento...
Funcionamento...
• Neste exemplo, usamos exceções Java para controlar a
interrupção da Thread
• Quando executamos o exemplo, o programa começa a passar
por pastas, verificando se elas têm ou não o arquivo
• Por exemplo, se você entrar na pasta bcd, o programa
terá três chamadas recursivas ao método
processDirectory()
• Quando se detecta que ele foi interrompido, ele lança uma
exceção InterruptedException e continua a execução no
método run(), não importa quantas chamadas recursivas
foram feitas
• A exceção InterruptedException é lançada por alguns
métodos Java relacionados com a API de concorrência, como
em sleep()
Adormecendo e Retomando Uma
Thread
• Às vezes, podemos estar interessado em interromper a
execução da Thread durante um determinado período de
tempo
– Por exemplo, uma thread em um programa verifica um estado de um
sensor uma vez por minuto, o resto do tempo, a thread não faz nada
– Durante este tempo, a thread não utiliza recursos do computador
– Após este tempo, a thread estará pronta para continuar com a sua
execução, quando a JVM escolhe-la para ser executada
• Podemos usar o método sleep() da classe Thread para
este fim
• Este método recebe um número inteiro como parâmetro
indicando o número de milissegundos que a thread suspende
a sua execução
Adormecendo e Retomando Uma
Thread
• Quando o tempo de “dormência” acaba, a thread
continua com a sua execução, na instrução após a
chamada do método sleep(), quando a JVM lhe atribui
tempo de CPU
• Outra possibilidade é a utilização do método sleep() de
um elemento de enumeração TimeUnit
• Este método utiliza o método sleep() da classe Thread
para colocar a thread atual para dormir, mas ele recebe o
parâmetro na unidade que ele representa e o converte
para milissegundos
• O exemplo a seguir utiliza o método sleep() para
mostrar a data atual a cada segundo
Programa Exemplo
1. Crie uma classe chamada FileClock e especifique que
ela implementa a interface Runnable.
public class FileClock implements Runnable {
2. Implemente o método run().
@Override
public void run() {
Programa Exemplo
3. Faça um laço com 10 iterações. Em cada iteração, criar
um objeto Date, escrevê-lo para o arquivo, e chamar o
método sleep() do atributo SECONDS da classe
TimeUnit para suspender a execução da thread por um
segundo. Com este valor, a thread ficará adormecida por
cerca de um segundo. Como o método sleep () pode
lançar uma exceção InterruptedException, temos
que incluir o código para capturá-la. É uma boa prática
incluir código que libera ou fecha os recursos que a
thread está usando quando ela é interrompida.
Programa Exemplo
for (int i = 0; i < 10; i++) {
System.out.printf("%sn", new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.printf("FileClock foi
interrompido!");
}
}
}
Programa Exemplo
4. Crie uma classe chamada FileMain que contém o
método main().
public class FileMain {
public static void main(String[] args) {
5. Crie um objeto da classe FileClock e a thread para
executá-lo. Então, inicie a execução da Thread.
FileClock clock=new FileClock();
Thread thread=new Thread(clock);
thread.start();
Programa Exemplo
6. Chame o método sleep() do atributo SECONDS da
classe TimeUnit na Thread principal para esperar por 5
segundos.
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
};
7. Interrompa a thread FileClock.
thread.interrupt();
8. Execute o exemplo e veja os resultados.
Funcionamento...
• Quando executamos o exemplo, podemos ver como o
programa escreve um objeto Date por segundo e, em
seguida, a mensagem indicando que a thread FileClock
foi interrompida é mostrada
Funcionamento...
• Quando chamamos o método sleep(), a Thread deixa a
CPU e para a sua execução por um período de tempo
• Durante este tempo, ela não está consumindo tempo de CPU,
de modo que o CPU pode executar outras tarefas
• Quando a Thread está dormindo e é interrompida, o método
gera uma exceção InterruptedException imediatamente
e não espera até que a hora de inatividade acabe
• A API de concorrência Java tem outro método que faz com
que um objeto Thread deixe a CPU, é o método yield(),
que indica à JVM que o objeto Thread pode liberar a CPU
para outras tarefas, A JVM não garante que ele irá cumprir
com este pedido
• Normalmente, ele é usado apenas para fins de depuração
Aguardando Pela Finalização de Uma
Thread
• Em algumas situações, temos que esperar pela
finalização de uma thread
• Por exemplo, podemos ter um programa que começará
inicializando os recursos que necessita antes de
prosseguir com o resto da execução
• Podemos executar as tarefas de inicialização como
threads e aguardar sua finalização antes de continuar
com o resto do programa
• Para isso, podemos usar o método join() da classe
Thread
• Quando chamamos este método usando um objeto
thread, ele suspende a execução da thread de chamada
até que o objeto chamado termine a sua execução
Programa Exemplo
1. Crie uma classe chamada DataSourcesLoader e
especifique que ela implementa a interface Runnable.
public class DataSourcesLoader implements
Runnable {
2. Implemente o método run(). Ele escreve uma
mensagem para indicar que iniciou a sua execução,
espera por 4 segundos, e escreve uma outra mensagem
para indicar que finalizou a sua execução.
Programa Exemplo
@Override
public void run() {
System.out.printf("Iniciando a carga de
recursos: %sn",new Date());
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf(“Carga de recursos
finalizada: %sn",new Date());
}
Programa Exemplo
3. Crie uma classe chamada NetworkConnectionsLoader
e especifique que ela implementa a interface Runnable.
Implemente o método run(). Será igual ao método de
run() da classe DataSourceLoader, mas esta vai
adormecer durante 6 segundos.
4. Agora crie uma classe chamada Main que contenha o
método main().
public class Main {
public static void main(String[] args) {
Programa Exemplo
5. Crie um objeto da classe DataSourcesLoader e uma
Thread para executá-lo.
DataSourcesLoader dsLoader = new
DataSourcesLoader();
Thread thread1 = new
Thread(dsLoader,"DataSourceThread");
6. Crie um objeto da classe NetworkConnectionsLoader
e uma Thread para executá-lo.
NetworkConnectionsLoader ncLoader = new
NetworkConnectionsLoader();
Thread thread2 = new
Thread(ncLoader,
"NetworkConnectionThread");
Programa Exemplo
7. Chame o método start() dos dois objetos Thread.
thread1.start();
thread2.start();
8. Aguarde a finalização de ambos os segmentos usando o
método join(). Este método pode lançar uma exceção
InterruptedException, por isso temos de incluir o
código para capturá-la.
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Programa Exemplo
9. Escreva uma mensagem para indicar o fim do programa.
System.out.printf("Main: A configuracao foi
carregada: %sn",new Date());
10. Execute o programa e veja os resultados.
Funcionamento...
• Quando executarmos este programa, podemos ver como
os dois objetos de Thread iniciam a sua execução
• Em primeiro lugar, a thread DataSourcesLoader termina
a sua execução
• Em seguida, a classe NetworkConnectionsLoader
termina a sua execução e, naquele momento, o objeto
Thread principal continua sua execução e escreve a
mensagem final
O Método join()
• Java fornece duas formas adicionais do método join():
– join (long milissegundos)
– join (long milissegundos, long nanos)
• Na primeira versão do método join(), em vez de esperar
indefinidamente pela finalização da thread chamada, a thread
que faz a chamada aguarda os milissegundos especificados
como um parâmetro do método
• Por exemplo, se o objeto thread1 tem o código,
thread2.join(1000), a thread thread1 suspende a sua
execução, até que uma destas duas condições seja verdadeira:
– thread2 termina a sua execução
– 1.000 milissegundos tenham passado
O Método join()
• A segunda versão do método join() é similar à primeira,
mas recebe o número de milissegundos e o número de
nano segundos como parâmetros
Referências
• González, J. F. Java 7 Concurrency Cookbook. Pack
Publishing, 2012.
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)

Programação Concorrente - Gerenciamento de Threads - Parte I

  • 1.
    UNIVERSIDADE ESTADUAL DOSUDOESTE 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 eexecutando uma thread • Recuperando e atribuindo informações de uma thread • Interrompendo uma thread • Controlando a interrupção de uma thread • Adormecendo e retomando uma thread • Aguardando pela finalização de uma thread
  • 4.
    Criando e ExecutandoUma Thread • Vamos aprender como criar e executar uma thread em uma aplicação Java • Tal como acontece com todos os elementos da linguagem Java, threads são objetos • Temos duas maneiras de criar uma thread em Java: – Estendendo a classe Thread e substituindo o método run() – A construção de uma classe que implementa a interface Runnable e, em seguida, criar um objeto da classe Thread passando o objeto que implementa a interface Runnable como um parâmetro • Vamos usar a segunda abordagem para criar um programa simples que cria e executa 10 threads • Cada thread calcula e imprime a tabela de multiplicação de um número entre um e 10
  • 5.
    Criando o Programa 1.Crie um novo projeto Java 2. Crie uma classe chamada Calculadora que implemente a interface Runnable public class Calculadora implements Runnable { 3. Declare um atributo private int chamado numero e implemente o construtor da classe inicializando o seu valor private int numero; public Calculadora(int numero) { this.numero=numero; }
  • 6.
    Criando o Programa 4.Implemente o método run(), este método irá executar as instruções da thread que criamos, assim, este método irá calcular a tabela de multiplicação para o número @Override public void run() { for (int i=1; i<=10; i++){ System.out.printf("%s: %d * %d = %dn", Thread.currentThread().getName(), numero,i,i*numero); } } 5. Agora implemente a classe principal da aplicação, criando uma classe Main que contém o método main() public class Main { public static void main(String[] args) {
  • 7.
    Criando o Programa 6.Dentro do método main() crie um laço for para 10 iterações, dentro do laço crie um objeto da classe Calculadora, um objeto da classe Thread, passe o objeto Calculadora como parâmetro, e chame o método start() do objeto thread for (int i=1; i<=10; i++){ Calculadora calc=new Calculadora(i); Thread thread=new Thread(calc); thread.start(); }
  • 8.
    Funcionamento... • Execute oprograma e veja como as diferentes threads trabalham em paralelo
  • 9.
    Funcionamento... • Cada programaJava tem pelo menos uma thread de execução • Quando executamos o programa, a JVM executa essa thread de execução que chama o método main() do programa • Quando chamamos o método start() de um objeto Thread, estamos criando uma outra thread de execução • Nosso programa terá tantas threads de execução quanto chamadas do método start() • Um programa Java termina quando todas as suas threads terminarem (mais especificamente, quando todas as suas threads não-daemon terminarem)
  • 10.
    Funcionamento... • Se athread inicial (aquele que executa o método main()) termina, o restante das threads irá continuar com a sua execução até que terminem • Se uma das threads chamar a instrução System.Exit() para terminar a execução do programa, todas as threads irão terminar a sua execução • Criar um objeto da classe Thread não cria uma nova thread de execução • Além disso, chamar o método run() de uma classe que implementa a interface Runnable não cria uma nova thread de execução • Apenas chamar o método start() cria uma nova thread de execução
  • 11.
    Funcionamento... • Como mencionadono início, há uma outra maneira de criar uma nova thread de execução: – Podemos implementar uma classe que estende a classe Thread e substituir o método run() da classe – Em seguida, podemos criar um objeto dessa classe e chamar o método start() para ter uma nova thread de execução
  • 13.
    Recuperando e AtribuindoInformações de Uma Thread • A classe Thread salva alguns atributos de informações que podem nos ajudar a identificar uma thread, saber o seu status, ou controlar a sua prioridade • Estes atributos são: – ID: armazena um identificador único para cada Thread – Nome: armazena o nome da Thread – Prioridade: armazena a prioridade dos objetos Thread. Threads podem ter uma prioridade entre um e dez, onde um é a prioridade mais baixa e dez é a mais alta. Não é recomendado alterar a prioridade das threads, mas é uma possibilidade que podemos usar se quisermos – Status: armazena o status da Thread. Em Java, Thread pode estar em um desses seis estados: new, runnable, blocked, waiting, time waiting, ou terminated
  • 14.
    Recuperando e AtribuindoInformações de Uma Thread • Neste exemplo, vamos desenvolver um programa que estabelece o nome e prioridade para 10 threads e, em seguida, mostra informações sobre o seu estado até que elas terminem • As threads irão calcular a tabela de multiplicação de um número
  • 15.
    Criando o Programa 1.Repita os passos de 1 a 5 do exemplo anterior para criação da classe Calculadora e do programa principal 6. Agora crie um array de 10 elementos Thread e um array com 10 elementos Thread.State para armazenar as threads que iremos executar e os seus status Thread threads[]=new Thread[10]; Thread.State status[]=new Thread.State[10];
  • 16.
    Criando o Programa 7.Crie 10 objetos da classe Calculadora, cada um inicializado com um número diferente, e 10 threads para executá-los, atribua valor máximo de prioridade a cinco delas e valor de prioridade mínimo às outras cinco for (int i=0; i<10; i++){ threads[i]=new Thread(new Calculadora(i+1)); if ((i%2)==0){ threads[i]. setPriority(Thread.MAX_PRIORITY); } else { threads[i]. setPriority(Thread.MIN_PRIORITY); } threads[i].setName("Thread "+i); }
  • 17.
    Criando o Programa 8.Crie um objeto PrintWriter para escrever em um arquivo a evolução do status das threads try ( FileWriter file = new FileWriter( ".log.txt"); // "./log.txt" no Linux! PrintWriter pw = new PrintWriter(file);) { // O resto do programa (itens 9 a 11) vai aqui! // ... } catch (IOException e) { e.printStackTrace(); } 9. Escreva neste arquivo o status das 10 threads for (int i=0; i<10; i++){ pw.println("Main : Status da Thread "+i+" : “ + threads[i].getState()); status[i]=threads[i].getState(); } 10. Inicie a execução das 10 threads for (int i=0; i<10; i++){ threads[i].start(); }
  • 18.
    Criando o Programa 11.Até que as 10 threads terminem, iremos checar os seus status, se detectarmos uma mudança no status da thread, iremos escrevê-la no arquivo boolean finish=false; while (!finish) { for (int i=0; i<10; i++){ if (threads[i].getState()!=status[i]) { writeThreadInfo(pw, threads[i],status[i]); status[i]=threads[i].getState(); } } finish=true; for (int i=0; i<10; i++){ finish=finish && (threads[i].getState()== Thread.State.TERMINATED); } } pw.close();
  • 19.
    Criando o Programa 12.Implemente o método writeThreadInfo() que escreve a ID, nome, prioridade, status anterior e novo status da thread private static void writeThreadInfo(PrintWriter pw, Thread thread, Thread.State state) { pw.printf("Main : Id %d - %sn",thread.getId(), thread.getName()); pw.printf("Main : Prioridade: %dn", thread.getPriority()); pw.printf("Main : Estado anterior: %sn", state); pw.printf("Main : Novo Estado: %sn", thread.getState()); pw.printf("Main : **********************" + "**************n"); }
  • 20.
    Criando o Programa •Execute o exemplo e abra o arquivo log.txt para ver a evolução das 10 threads
  • 21.
    Funcionamento... • O programamostra no console as tabuadas calculadas pelas threads e a evolução do estado das diferentes threads no arquivo log.txt, desta forma podemos visualizar melhor a evolução das threads • A classe Thread tem atributos para armazenar todas as informações de uma thread • A JVM utiliza a prioridade das threads para selecionar aquela que usa a CPU em cada momento e atualiza o status de cada thread de acordo com a sua situação • Se não especificarmos um nome para uma thread, a JVM automaticamente atribui a ela um nome com o formato, Thread-XX, onde XX é um número • Não podemos modificar a ID ou o status de uma thread, a classe Thread não implementa os métodos setId() e setStatus() para permitir a sua alteração
  • 22.
    Funcionamento... • Também podemosacessar os atributos de uma thread a partir da implementação da interface Runnable, podemos usar o método estático CurrentThread() da classe Thread para acessar o objeto Thread que está executando o objeto Runnable • Devemos levar em conta que o método setPriority() pode lançar uma exceção IllegalArgumentException se tentarmos estabelecer uma prioridade que não está entre 1 e 10
  • 24.
    Interrompendo Uma Thread •Um programa Java com mais de uma thread de execução só termina quando a execução de todas as suas threads e, mais especificamente, quando todas as suas threads não-daemon terminam a sua execução ou quando uma das threads usam o método System.exit() • Às vezes, vamos precisar terminar uma thread, porque queremos encerrar um programa, ou quando um usuário do programa quer cancelar as tarefas que um objeto Thread está fazendo • Java fornece um mecanismo de interrupção para indicar a uma thread que queremos terminá-la
  • 25.
    Interrompendo Uma Thread •Uma peculiaridade deste mecanismo é que a Thread tem que verificar se ela foi interrompida ou não, e pode decidir se ela responde ao pedido de finalização ou não • A Thread pode ignorá-lo e continuar com a sua execução
  • 26.
    Programa Exemplo 1. Crieuma classe chamada PrimeGenerator que estende a classe Thread public class PrimeGenerator extends Thread{ 2. Sobrescreva o método run() incluindo um laço que irá rodar indefinidamente. Neste laço iremos processar números consecutivos iniciando por um. Para cada número iremos calcular se ele é primo e, neste caso, iremos imprimi-lo no console. @Override public void run() { long number=1L; while (true) { if (isPrime(number)) { System.out.printf("O numero %d eh Primon", number); } ...
  • 27.
    Programa Exemplo 3. Apósprocessar um número, verifique se a thread foi interrompida através do método isInterrupted(). Se o método retornar true, escreveremos uma mensagem e terminaremos a execução da thread. if (isInterrupted()) { System.out.printf("O Gerador de Primos foi interrompido!n"); return; } number++; } }
  • 28.
    Programa Exemplo 4. Implementeo método isPrime(). Ele irá retornar um valor boolean indicando se o número recebido como parâmetro é um número primo (true) ou não (false). private boolean isPrime(long number) { if (number <=2) { return true; } for (long i=2; i<number; i++){ if ((number % i)==0) { return false; } } return true; }
  • 29.
    Programa Exemplo 5. Agoraimplemente a classe principal do exemplo (Main), implementando o método main(). public class Main { public static void main(String[] args) { 6. Crie e inicie um objeto da classe PrimeGenerator. Thread task=new PrimeGenerator(); task.start(); 7. Espere por 5 segundos e interrompa a thread PrimeGenerator. try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } task.interrupt(); 8. Rode o programa e veja os resultados.
  • 30.
  • 31.
    Funcionamento... • A classeThread tem um atributo que armazena um valor booleano que indica se a thread foi interrompida ou não • Quando chamamos o método interrupt() de uma thread, definimos esse atributo para true • O método isInterrupted() retorna apenas o valor do atributo • A classe Thread tem outro método para verificar se foi interrompida ou não, é o método estático, interrupted(), que verifica se a thread atual em execução foi interrompida ou não – O método interrupted(), diferentemente de isInterrupted(), modifica o valor do atributo interrupted para false – Por se tratar de um método estático, a sua utilização não é recomendada
  • 33.
    Controlando a Interrupçãode Uma Thread • O mecanismo mostrado no exemplo anterior, pode ser utilizado se a thread que pode ser interrompida é simples, mas se a thread implementa um algoritmo complexo dividido em alguns métodos, ou se tem métodos com chamadas recursivas, podemos usar um mecanismo melhor para controlar a interrupção da thread • Java fornece a exceção InterruptedException com esta finalidade, podemos lançar essa exceção quando detectarmos a interrupção da thread e capturá-la no método run()
  • 34.
    Controlando a Interrupçãode Uma Thread • Neste exemplo, vamos implementar uma Thread que procura arquivos com um determinado nome em uma pasta e em todas as suas subpastas para mostrar como usar a exceção InterruptedException para controlar a interrupção de uma thread
  • 35.
    Programa Exemplo 1. Crieuma classe chamada FileSearch e especifique que ela implementa a interface Runnable. public class FileSearch implements Runnable { 2. Declare dois atributos privados, um para o nome do arquivo que será procurado e outro para a pasta inicial. Implemente o construtor dessa classe, que inicializa esses atributos. private String initPath; private String fileName; public FileSearch(String initPath, String fileName) { this.initPath = initPath; this.fileName = fileName; }
  • 36.
    Programa Exemplo 3. Implementeo método run() da classe FileSearch. Ele verifica se o atributo initPath é uma pasta e, se for, chama o método processDirectory(). Este método pode lançar uma exceção InterruptedException, devendo capturá-la. @Override public void run() { File file = new File(initPath); if (file.isDirectory()) { try { directoryProcess(file); } catch (InterruptedException e) { System.out.printf("%s: A busca foi interrompida", Thread.currentThread().getName()); } } }
  • 37.
    Programa Exemplo 4. Implementeo método directoryProcess(). Este método irá obter e processar os arquivos e subpastas da pasta. Para cada subpasta, o método irá fazer uma chamada recursiva passando a pasta como parâmetro. Para cada arquivo, o método irá chamar o métodos fileProcess(). Após processar todos os arquivos e pastas, o método verifica se a Thread foi interrompida e, neste caso, lança uma exceção InterruptedException.
  • 38.
    Programa Exemplo private voiddirectoryProcess(File file) throws InterruptedException { System.out.printf("Processando %s ...n", file.getName()); File list[] = file.listFiles(); if (list != null) { for (int i = 0; i < list.length; i++) { if (list[i].isDirectory()) { directoryProcess(list[i]); } else { fileProcess(list[i]); } } } if (Thread.interrupted()) { throw new InterruptedException(); } }
  • 39.
    Programa Exemplo 5. Implementeo método processFile(). Este método irá comparar o nome do arquivo que está sendo processado com o nome que estamos buscando. Se forem iguais, iremos escrever uma mensagem no console. Após esta comparação, a Thread irá verificar se foi interrompida e, neste caso, irá lançar uma exceção InterruptedException. private void fileProcess(File file) throws InterruptedException { if (file.getName().equals(fileName)) { System.out.printf("%s : %s !!!n", Thread.currentThread().getName(), file.getAbsolutePath()); } if (Thread.interrupted()) { throw new InterruptedException(); } }
  • 40.
    Programa Exemplo 6. Agoravamos implementar a classe principal do exemplo. Implemente a classe Main que contém o método main(). public class Main { public static void main(String[] args) { 7. Crie e inicialize um objeto da classe FileSearch e uma Thread para executar a sua tarefa. Então inicie a execução da Thread. FileSearch searcher=new FileSearch("C:","autoexec.bat"); // Modificar! Principalmente no Linux… Thread thread=new Thread(searcher); thread.start();
  • 41.
    Programa Exemplo 8. Aguardepor 10 segundos e interrompa a Thread. try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt(); } 9. Execute o programa e veja os resultados.
  • 42.
  • 43.
    Funcionamento... • Neste exemplo,usamos exceções Java para controlar a interrupção da Thread • Quando executamos o exemplo, o programa começa a passar por pastas, verificando se elas têm ou não o arquivo • Por exemplo, se você entrar na pasta bcd, o programa terá três chamadas recursivas ao método processDirectory() • Quando se detecta que ele foi interrompido, ele lança uma exceção InterruptedException e continua a execução no método run(), não importa quantas chamadas recursivas foram feitas • A exceção InterruptedException é lançada por alguns métodos Java relacionados com a API de concorrência, como em sleep()
  • 45.
    Adormecendo e RetomandoUma Thread • Às vezes, podemos estar interessado em interromper a execução da Thread durante um determinado período de tempo – Por exemplo, uma thread em um programa verifica um estado de um sensor uma vez por minuto, o resto do tempo, a thread não faz nada – Durante este tempo, a thread não utiliza recursos do computador – Após este tempo, a thread estará pronta para continuar com a sua execução, quando a JVM escolhe-la para ser executada • Podemos usar o método sleep() da classe Thread para este fim • Este método recebe um número inteiro como parâmetro indicando o número de milissegundos que a thread suspende a sua execução
  • 46.
    Adormecendo e RetomandoUma Thread • Quando o tempo de “dormência” acaba, a thread continua com a sua execução, na instrução após a chamada do método sleep(), quando a JVM lhe atribui tempo de CPU • Outra possibilidade é a utilização do método sleep() de um elemento de enumeração TimeUnit • Este método utiliza o método sleep() da classe Thread para colocar a thread atual para dormir, mas ele recebe o parâmetro na unidade que ele representa e o converte para milissegundos • O exemplo a seguir utiliza o método sleep() para mostrar a data atual a cada segundo
  • 47.
    Programa Exemplo 1. Crieuma classe chamada FileClock e especifique que ela implementa a interface Runnable. public class FileClock implements Runnable { 2. Implemente o método run(). @Override public void run() {
  • 48.
    Programa Exemplo 3. Façaum laço com 10 iterações. Em cada iteração, criar um objeto Date, escrevê-lo para o arquivo, e chamar o método sleep() do atributo SECONDS da classe TimeUnit para suspender a execução da thread por um segundo. Com este valor, a thread ficará adormecida por cerca de um segundo. Como o método sleep () pode lançar uma exceção InterruptedException, temos que incluir o código para capturá-la. É uma boa prática incluir código que libera ou fecha os recursos que a thread está usando quando ela é interrompida.
  • 49.
    Programa Exemplo for (inti = 0; i < 10; i++) { System.out.printf("%sn", new Date()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.printf("FileClock foi interrompido!"); } } }
  • 50.
    Programa Exemplo 4. Crieuma classe chamada FileMain que contém o método main(). public class FileMain { public static void main(String[] args) { 5. Crie um objeto da classe FileClock e a thread para executá-lo. Então, inicie a execução da Thread. FileClock clock=new FileClock(); Thread thread=new Thread(clock); thread.start();
  • 51.
    Programa Exemplo 6. Chameo método sleep() do atributo SECONDS da classe TimeUnit na Thread principal para esperar por 5 segundos. try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }; 7. Interrompa a thread FileClock. thread.interrupt(); 8. Execute o exemplo e veja os resultados.
  • 52.
    Funcionamento... • Quando executamoso exemplo, podemos ver como o programa escreve um objeto Date por segundo e, em seguida, a mensagem indicando que a thread FileClock foi interrompida é mostrada
  • 53.
    Funcionamento... • Quando chamamoso método sleep(), a Thread deixa a CPU e para a sua execução por um período de tempo • Durante este tempo, ela não está consumindo tempo de CPU, de modo que o CPU pode executar outras tarefas • Quando a Thread está dormindo e é interrompida, o método gera uma exceção InterruptedException imediatamente e não espera até que a hora de inatividade acabe • A API de concorrência Java tem outro método que faz com que um objeto Thread deixe a CPU, é o método yield(), que indica à JVM que o objeto Thread pode liberar a CPU para outras tarefas, A JVM não garante que ele irá cumprir com este pedido • Normalmente, ele é usado apenas para fins de depuração
  • 55.
    Aguardando Pela Finalizaçãode Uma Thread • Em algumas situações, temos que esperar pela finalização de uma thread • Por exemplo, podemos ter um programa que começará inicializando os recursos que necessita antes de prosseguir com o resto da execução • Podemos executar as tarefas de inicialização como threads e aguardar sua finalização antes de continuar com o resto do programa • Para isso, podemos usar o método join() da classe Thread • Quando chamamos este método usando um objeto thread, ele suspende a execução da thread de chamada até que o objeto chamado termine a sua execução
  • 56.
    Programa Exemplo 1. Crieuma classe chamada DataSourcesLoader e especifique que ela implementa a interface Runnable. public class DataSourcesLoader implements Runnable { 2. Implemente o método run(). Ele escreve uma mensagem para indicar que iniciou a sua execução, espera por 4 segundos, e escreve uma outra mensagem para indicar que finalizou a sua execução.
  • 57.
    Programa Exemplo @Override public voidrun() { System.out.printf("Iniciando a carga de recursos: %sn",new Date()); try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf(“Carga de recursos finalizada: %sn",new Date()); }
  • 58.
    Programa Exemplo 3. Crieuma classe chamada NetworkConnectionsLoader e especifique que ela implementa a interface Runnable. Implemente o método run(). Será igual ao método de run() da classe DataSourceLoader, mas esta vai adormecer durante 6 segundos. 4. Agora crie uma classe chamada Main que contenha o método main(). public class Main { public static void main(String[] args) {
  • 59.
    Programa Exemplo 5. Crieum objeto da classe DataSourcesLoader e uma Thread para executá-lo. DataSourcesLoader dsLoader = new DataSourcesLoader(); Thread thread1 = new Thread(dsLoader,"DataSourceThread"); 6. Crie um objeto da classe NetworkConnectionsLoader e uma Thread para executá-lo. NetworkConnectionsLoader ncLoader = new NetworkConnectionsLoader(); Thread thread2 = new Thread(ncLoader, "NetworkConnectionThread");
  • 60.
    Programa Exemplo 7. Chameo método start() dos dois objetos Thread. thread1.start(); thread2.start(); 8. Aguarde a finalização de ambos os segmentos usando o método join(). Este método pode lançar uma exceção InterruptedException, por isso temos de incluir o código para capturá-la. try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); }
  • 61.
    Programa Exemplo 9. Escrevauma mensagem para indicar o fim do programa. System.out.printf("Main: A configuracao foi carregada: %sn",new Date()); 10. Execute o programa e veja os resultados.
  • 62.
    Funcionamento... • Quando executarmoseste programa, podemos ver como os dois objetos de Thread iniciam a sua execução • Em primeiro lugar, a thread DataSourcesLoader termina a sua execução • Em seguida, a classe NetworkConnectionsLoader termina a sua execução e, naquele momento, o objeto Thread principal continua sua execução e escreve a mensagem final
  • 63.
    O Método join() •Java fornece duas formas adicionais do método join(): – join (long milissegundos) – join (long milissegundos, long nanos) • Na primeira versão do método join(), em vez de esperar indefinidamente pela finalização da thread chamada, a thread que faz a chamada aguarda os milissegundos especificados como um parâmetro do método • Por exemplo, se o objeto thread1 tem o código, thread2.join(1000), a thread thread1 suspende a sua execução, até que uma destas duas condições seja verdadeira: – thread2 termina a sua execução – 1.000 milissegundos tenham passado
  • 64.
    O Método join() •A segunda versão do método join() é similar à primeira, mas recebe o número de milissegundos e o número de nano segundos como parâmetros
  • 65.
    Referências • González, J.F. Java 7 Concurrency Cookbook. Pack Publishing, 2012.
  • 66.
    UNIVERSIDADE ESTADUAL DOSUDOESTE DA BAHIA CURSO DE CIÊNCIA DA COMPUTAÇÃO PROGRAMAÇÃO CONCORRENTE – 2015.1 Fábio M. Pereira (fabio.mpereira@uesb.edu.br)