Escolar Documentos
Profissional Documentos
Cultura Documentos
Java Avancado
Java Avancado
Java Avanado
Multithreading
Prof. Jucimar Souza jucibs@gmail.com
Objetivos
O que so threads e por que elas so teis O ciclo de vida de uma thread Prioridades e agendamento de threads Como criar e executar Runnables Sincronizao de Thread
07/11/2008
1 - Introduo
Multithreading:
Fornece mltiplas threads de execuo para a aplicao. Permite que programas realizem tarefas concorrentemente. Com freqncia, exige que o programador sincronize as threads para que funcionem corretamente.
Estados de thread:
Estado novo:
Uma nova thread inicia seu ciclo de vida no estado novo. Permanece nesse estado at o programa iniciar a thread, colocando-a no estado executvel
Estado executvel:
Uma thread que entra nesse estado est executando sua tarefa.
Estado em espera:
Uma thread entra nesse estado a fim de esperar que uma outra thread realize uma tarefa.
07/11/2008
Estados de thread:
Estado de espera cronometrada:
Uma thread entra nesse estado para esperar uma outra thread ou para transcorrer um determinado perodo de tempo. Uma thread nesse estado retorna ao estado executvel quando ela sinalizada por uma outra thread ou quando o intervalo de tempo especificado expirar.
Estado terminado:
Uma thread executvel entra nesse estado quando completa sua tarefa.
07/11/2008
executvel:
Estado pronto:
Uma thread nesse estado no est esperando uma outra thread, mas est esperando que o sistema operacional atribua a thread a um processador.
Estado em execuo:
Uma thread nesse estado tem atualmente um processador e est executando.
Uma thread no estado em execuo freqentemente utiliza uma pequena quantidade de tempo de processador chamada frao de tempo, ou quantum, antes de migrar de volta para o estado pronto.
07/11/2008
Prioridades:
Cada thread Java tem uma prioridade. As prioridades do Java esto no intervalo entre MIN_PRIORITY (uma constante de 1) e MAX_PRIORITY (uma constante de 10). As threads com uma prioridade mais alta so mais importantes e tero um processador alocado antes das threads com uma prioridade mais baixa. A prioridade-padro NORM_PRIORITY (uma constante de 5).
Agendador de thread:
Determina qual thread executada em seguida. Uma implementao simples executa threads com a mesma prioridade no estilo rodzio. Threads de prioridade mais alta podem fazer preempo da thread atualmente em execuo. Em alguns casos, as threads de prioridade alta podem adiar indefinidamente threads de prioridade mais baixa o que tambm conhecido como inanio.
07/11/2008
A interface Runnable:
Meio preferido de criar um aplicativo com multithreads. Declara o mtodo run. Executado por um objeto que implementa a interface Executor.
Interface Executor:
Declara o mtodo execute. Cria e gerencia um grupo de threads chamado
pool de threads.
Classe Executors:
O mtodo newFixedThreadPool cria um pool que consiste em um nmero fixo de threads. O mtodo newCachedThreadPool cria um pool que cria novas threads conforme necessrio.
07/11/2008
Vejamos o exemplo ExThreadUm.java. Ele composto de duas classe que implementam Runnable. No programa principal instanciado um ExecutorThread para gerenciar as threads. O mtodo execute da ExecutorThread d incio a execuo da thread.
Vamos analisar o exemplo PrintTask.java e RunnableTester.java. O PrintTask utiliza uma classe para gerar nmeros aleatrios (Random) e este numero ser colocado na thread como um tempo em milisegundos que a thread ficar adormecida
07/11/2008
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Fig. 23.4: PrintTask.java // Classe PrintTask dorme por um tempo aleatrio de 0 a 5 segundos import java.util.Random; class PrintTask implements Runnable {
private int sleepTime; // tempo de adormecimento aleatrio para a thread nome private String threadName; // nome da thread private static Random generator = new Random(); // atribui nome thread public PrintTask( String name ) { da threadName = name; // configura nome da thread // seleciona tempo de adormecimento aleatrio entre 0 e 5 segundos sleepTime = generator.nextInt( 5000 ); } // fim do construtor PrintTask
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
// mtodo run o cdigo a ser executado pela nova thread public void run() {
Declara de tempo sleepTime try // coloca a thread para dormir a pela quantidade o mtodo run para atender a
{ threadName, sleepTime ); Thread.sleep( sleepTime ); // coloca a thread para dormir } // fim do try // se a thread foi interrompida enquanto dormia, imprime o rastreamento de pilha catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch // imprime o nome da thread System.out.printf( "%s acordou\n", threadName ); System.out.printf( acordou\n", } // fim do mtodo run
interface
System.out.printf( milisegundos. n", System.out.printf( "%s dormindo por %d milisegundos.\n",
07/11/2008
1 2 3 4 5 6 7 8
// Fig. 23.5: RunnableTester.java // Impresso de mltiplas threads em diferentes intervalos. intervalos. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService;
public class RunnableTester { public static void main( String[] args ) 9 { 10 // cria e nomeia cada executvel 11 PrintTask task1 = new PrintTask( "thread1" ); 12 PrintTask task2 = new PrintTask( "thread2" ); 13 PrintTask task3 = new PrintTask( "thread3" ); 14 15 16 17 18 19 20 21 22 23 24 25 26 System.out.println( "Starting threads" );
// cria ExecutorService para gerenciar threads Executa cada tarefa; ExecutorService threadExecutor = Executors.newFixedThreadPool( 3 );esse mtodo coloca // inicia threads e coloca no estado executvel threadExecutor.execute( task1 ); // inicia task1 Desativa o pool de threads quando os threadExecutor.execute( task2 ); // inicia task2 runnables completarem suas tarefas threadExecutor.execute( task3 ); // inicia task3 encerra threadExecutor.shutdown(); // encerra as threads trabalhadoras
System.out.println( "Threads started, main ends\n" ); ends\ } // fim do main 29 } // fim da classe RunnableTester 27 28 "Startandos" as threads Startandos" startadas" Threads "startadas", Fim do programa principal thread1 thread1 thread2 thread2 thread3 thread3 thread3 thread3 thread1 thread1 thread2 thread2 milliseconds. dormindo por 2493 milliseconds. milliseconds. dormindo por 3966 milliseconds. milliseconds. dormindo por 2348 milliseconds. acordou acordou acordou
"Startandos" as threads Startandos" startadas" Threads "startadas", Fim do programa principal thread1 thread1 thread2 thread2 thread3 thread3 thread1 thread1 thread3 thread3 thread2 thread2 milliseconds. dormindo por 3103 milliseconds. milliseconds. dormindo por 4977 milliseconds. milliseconds. dormindo por 3492 milliseconds. acordou acordou acordou
07/11/2008
Relacionamento produtor/consumidor:
O produtor gera dados e os armazena na memria compartilhada. O consumidor l os dados da memria compartilhada. A memria compartilhada chamada buffer. Utliza o mtodo sleep da Thread para deixa-la sem atividade por at 3 segundos
1 2 3 4 5 6 7 8
Consumer. // Interface Buffer especifica mtodos chamados por Producer e Consumer. public interface Buffer { public void set( int value ); // coloca o valor int no Buffer valor public int get(); // retorna o valor int a partir do Buffer } // fim da interface Buffer
Resumo
Buffer.java
Figura 23.6
10
07/11/2008
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Resumo
public class Producer implements Runnable { private static Random generator = new Random(); Buffer private Buffer sharedLocation; // referncia a objeto de modo que o compartilhado construtor // construtor public Producer( Buffer shared ) { sharedLocation = shared; } // fim do construtor Producer valo sharedLocation // armazena valores de 1 a 10 em sharedLocation public void run() { int sum = 0;
Implementa a interface runnable produtor possa ser executado em uma thread separada
Producer .java
(1 de 2)
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
10; for ( int count = 1; count <= 10; count++ ) { { Thread.sleep( generator.nextInt( 3000 ) ); // thread sleep sharedLocation.set( sharedLocation.set( count ); // configura valor no buffer incrementa valores sum += count; // incrementa soma de valores t%2d\n", System.out.printf( "\t%2d\n", sum ); } // fim do try catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch } // fim do for n%s\n%s\n", producing.", System.out.printf( "\n%s\n%s\n", "Producer done producing.", "Terminating Producer." ); mtodo } // fim do mtodo run try // dorme de 0 a 3 segundos, ento coloca valor em Buffer
Resumo
(2 de 2)
11
07/11/2008
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// Fig. 23.8: Consumer.java buffer. // O mtodo run de Consumer itera dez vezes lendo um valor do buffer. import java.util.Random;
Resumo
public class Consumer implements Runnable { private static Random generator = new Random(); Implementa a interface private private Buffer sharedLocation; // referncia a objeto compartilhado // construtor public Consumer( Buffer shared ) { sharedLocation = shared; } // fim do construtor Consumer
runnable de modo que o produtor possa ser executado em uma thread separada
Consum er.java
(1 de 2)
Declara o mtodo run para a interface
satisfazer sharedLocation // l o valor do sharedLocation quatro vezes e soma os valores public void run() {
int sum = 0;
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
10; for ( int count = 1; count <= 10; count++ ) { // dorme de 0 a 3 segundos, l o valor do buffer e adiciona a soma try { Thread.sleep( generator.nextInt( 3000 ) ); sum += sharedLocation.get();
Resumo
System.out.printf( "\t\t\t%2d\n", sum ); t%2d\n", } // fim do try // se a thread adormecida interrompida, imprime rastreamento at 3 segundos Dorme por de pilha catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch } // fim do for
Consum er.java
(2 de 2)
%d.\n%s\n", System.out.printf( "\n%s %d.\n%s\n", "Consumer read values totaling", sum, "Terminating Consumer." ); totaling", 39 mtodo } // fim do mtodo run 40 } // fim da classe Consumer
12
07/11/2008
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Resumo
public class UnsynchronizedBuffer implements Buffer { consumer private int buffer = -1; // compartilhado pelas threads producer e consumer // coloca o valor no buffer public void set( int value ) { System.out.printf( "Producer writes\t%2d", value ); writes\t%2d", buffer = value; } // fim do mtodo set retorna buffer // retorna o valor do buffer public int get() {
System.out.printf( "Consumer reads\t%2d", buffer ); reads\t%2d", return buffer; } // fim do mtodo get 21 } // fim da classe UnsynchronizedBuffer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Fig 23.10: SharedBufferTest.java // Aplicativo mostra duas threads que manipulam um buffer no-sincronizado. no- sincronizado. import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SharedBufferTest { public static void main( String[] args ) { // cria novo pool de threads com duas threads ExecutorService application = Executors.newFixedThreadPool( 2 ); // cria UnsynchronizedBuffer para armazenar ints Buffer sharedLocation = new UnsynchronizedBuffer();
Resumo
(1 de 4)
13
07/11/2008
16 17 18 19 20 21 22 23 24 25 26 27 28 29
"Action\ tValue\tProduced\ System.out.println( "Action\t\tValue\tProduced\tConsumed" ); System.out.println( "------\t\t-----\t--------\t--------\n" ); ------\ -----\ --------\ --------\
Resumo
// tenta iniciar as threads produtora e consumidora fornecendo acesso a cada uma // para sharedLocation try {
application.execute( new Producer( sharedLocation ) ); application.execute( new Consumer( sharedLocation ) ); } // fim do try catch ( Exception exception ) { Passa o buffer compartilhado tanto exception.printStackTrace(); para o produtor como para o } // fim do catch
consumidor 30 31 aplicativo application.shutdown(); // termina aplicativo quando as threads terminam 32 } // fim do main 33 } // fim da classe SharedBufferTest
(2 de 4)
Action -----Producer Producer Producer Consumer Producer Consumer Producer Producer Producer Consumer Consumer Producer Consumer Consumer Producer Producer
Resumo
Value ----writes 1 writes 2 writes 3 reads 3 writes 4 reads 4 writes 5 writes 6 writes 7 reads 7 reads 7 writes 8 reads 8 reads 8 writes 9 writes 10 1 3 6 10
Produced --------
Consumed --------
3 7 15 21 28 14 21 36 29 37 45 55
(3 de 4)
Producer done producing. Terminating Producer. reads Consumer reads 10 Consumer reads 10 Consumer reads 10 Consumer reads 10 Consumer read values totaling 77. Terminating Consumer.
47 57 67 77
14
07/11/2008
Action -----Consumer Producer Consumer Consumer Consumer Consumer Consumer Producer Consumer Producer Consumer Producer Consumer Producer Producer Consumer
Resumo
Value ----reads -1 writes 1 reads 1 reads 1 reads 1 reads 1 reads 1 writes 2 reads 2 writes 3 reads 3 writes 4 reads 4 writes 5 writes 6 6 reads 1
Produced --------
Consumed --------1 0 1 2 3 4
3 6 6 9 10 13 15 21 19
(4 de 4)
Consumer read values totaling 19. Terminating Consumer. Producer writes 7 Producer 28 Producer writes 8 36 Producer writes 9 45 Producer writes 10 55 Producer done producing. Terminating Producer.
Interface Lock:
O mtodo lock obtm o bloqueio, impondo a excluso mtua. O mtodo unlock libera o bloqueio. A classe ReentrantLock implementa a interface Lock.
15
07/11/2008
Interface Condition:
Declara os mtodos: await, para fazer uma thread esperar; signal, para acordar uma thread em espera; e signalAll, para acordar todas as threads em espera.
5 - Sincronizao de thread (cont) O impasse (deadlock) ocorre quando uma thread em espera (vamos cham-la de thread1) no pode prosseguir porque est esperando (direta ou indiretamente) outra thread (vamos cham-la de thread2) prosseguir; simultaneamente, a thread2 no pode prosseguir porque est esperando (direta ou indiretamente) a thread1 prosseguir. Como duas threads esto esperando uma outra, as aes que permitiriam a cada thread continuar a execuo nunca ocorrem.
16
07/11/2008
Relacionamento produtor/consumidor:
Este exemplo utiliza Locks e Conditions para implementar a sincronizao. Este programa tem o objetivo consumir todos os numeros gerados pelo produtor. Como o produtor pode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// Fig. 23.11: SynchronizedBuffer.java compartilhado. // SynchronizedBuffer sincroniza acesso a um nico inteiro compartilhado. import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition; public class SynchronizedBuffer implements Buffer { private Lock accessLock = new ReentrantLock(); gravao // condies para controlar leitura e gravao
Resumo
Synchro nizedBuff Cria duas variveis de Condition; uma para gravao e er outra para leitura .java
produtor consumidor
private int buffer = -1; // compartilhado pelas threads producer e consumer por Buffer compartilhado false; private boolean occupied = false; // se o buffer estiver ocupadoe // coloca o valor int no buffer public void set( int value ) { accessLock.lock(); // bloqueia esse objeto
(1 de 5)
17
07/11/2008
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
Resumo
// enquanto o buffer no estiver vazio, coloca thread no estado de espera while ( occupied ) { System.out.println( "Producer tries to write." ); displayState( "Buffer full. Producer waits." ); } // end while
// indica que a produtora no pode armazenar outro valor // at a consumidora recuperar valor atual de buffer true; occupied = true;
(2 de 5)
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
displayState( "Producer writes " + buffer ); // sinaliza a thread que est esperando para ler a partir do buffer canRead.signal(); } // fim do try InterruptedException catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch finally { accessLock.unlock(); // desbloqueia esse objeto } // fim do finally } // fim do mtodo set do // retorna valor do buffer public int get() { int readValue = 0; // inicializa de valor lido a partir do buffer accessLock.lock(); // bloqueia esse objeto
Resumo
(3 de 5)
Adquire o bloqueio antes de ler um valor
18
07/11/2008
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
// envia informaes de thread e de buffer para a sada, ento espera try { // enquanto os dados no so lidos, coloca thread em estado de espera while ( !occupied ) {
Resumo
System.out.println( "Consumer tries to read." ); displayState( "Buffer empty. Consumer waits." ); tornarcanRead.await(); // espera at o buffer tornar-se cheio O consumidor } // fim do while que // indica que a produtora pode armazenar outro valor // porque a consumidora acabou de recuperar o valor do buffer occupied = false; false; readValue = buffer; // recupera o valor do buffer displayState( "Consumer reads " + readValue );
(4 de 5)
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
} // fim do try gravar no // se a thread na espera tiver sido interrompida, imprime o rastreamento de pilha pilha catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch finally { objeto accessLock.unlock(); // desbloqueia esse objeto } // fim do finally return readValue; } // fim do mtodo get
tornar// sinaliza a thread que est esperando o buffer tornar-se vazio canWrite.signal(); Sinaliza ao produtor
Resumo
// exibe a operao atual e o estado de buffer public void displayState( String operation ) { "%-40s%d\ t%b\ n", System.out.printf( "%-40s%d\t\t%b\n\n", operation, buffer, occupied );
(5 de 5)
19