Você está na página 1de 19

07/11/2008

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.

2 - Estados de thread: Classe Thread

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

2 - Estados de thread: Classe Thread (Cont)

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.

2.1 Diagrama de estado do ciclo de vida da thread

07/11/2008

2 - Estados de thread: Classe Thread (Cont) Viso do sistema operacional do estado

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.

2 - Estados de thread: Classe Thread (Cont)

Visualizao interna do sistema operacional do estado executvel do Java

07/11/2008

3 - Prioridades e agendamento de thread

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).

3 - Prioridades e agendamento de thread

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

4 - Criando e executando threads

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.

4 - Criando e executando threads Interface ExecutorService:


uma subinterface de Executor que declara outros mtodos para gerenciar o ciclo de vida de um Executor. Pode ser criada utilizando os mtodos static da classe Executors. O mtodo shutdown finaliza as threads quando as tarefas so concludas.

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

4 - Criando e executando threads

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.

4 - Criando e executando threads

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 {

Implementa runnable para criar uma thread separada

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",

39 } // fim da classe PrintTask

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 trs PrintTasks; cada uma executar em uma thread separada

Cria um pool de threads fixas para executar e gerenciar 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

atribuir uma thread a runnable

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

6 - Relacionamento entre produtor e consumidor sem sincronizao

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

// Fig. 23.6: Buffer.java

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

| Interface Buffer utilizada nos exemplos de produtor/consumidor.

10

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.7: Producer.java import java.util.Random;

buffer. // O mtodo run do Producer armazena os valores de 1 a 10 no buffer.

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

Declara o mtodo run para satisfazer a interface

(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

// se a thread adormecida interrompida, imprime rastreamento de pilha

Producer Dorme por at 3 segundos .java

(2 de 2)

40 } // fim da classe Producer

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

// Fig. 23.9: UnsynchronizedBuffer.java compartilhado. // UnsynchronizedBuffer representa um nico inteiro compartilhado.

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() {

Varivel compartilhada para armazenar dados

Unsynch ronized Buffer.jav Configura o valor do buffer a


L o valor do buffer

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

SharedB ufferTest .java

(1 de 4)

Cria um UnsynchronizedBuffer compartilhado para que o produtor e o consumidor o utilizem

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

SharedB ufferTest .java

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

SharedB ufferTest .java

(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

SharedB ufferTest .java

(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.

5 - Sincronizao de thread Sincronizao de threads:


Fornecido ao programador com excluso mtua.
Acesso exclusivo a um objeto compartilhado.

Implementado no Java utilizando bloqueios.

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

5 - Sincronizao de thread (cont) Variveis de condio:


Se uma thread que mantm o bloqueio no puder continuar a sua tarefa at uma condio ser satisfeita, a thread pode esperar uma varivel de condio. Criadas chamando newCondition do mtodo Lock. Representadas por um objeto que implementa a interface Condition.

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

7 - Relacionamento entre produtor e consumidor com sincronizao

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

// Bloqueio para controlar sincronizao com esse buffer

private Condition canWrite = accessLock.newCondition(); private Condition canRead = accessLock.newCondition();

Synchro nizedBuff Cria duas variveis de Condition; uma para gravao e er outra para leitura .java
produtor consumidor

Cria ReentrantLock para excluso mtua

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)

Tenta obter o bloqueio antes de configurar o valor dos dados compartilhados

17

07/11/2008

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

// envia informaes de thread e de buffer para a sada, ento espera try {

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

canWrite.await(); // espera at que o buffer esteja vazio

buffer = value; // configura novo valor de buffer value;

// indica que a produtora no pode armazenar outro valor // at a consumidora recuperar valor atual de buffer true; occupied = true;

Synchro Produtor espera at que o buffer nizedBuff esteja vazio er .java

(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

Sinaliza ao consumidor que ele pode ler um valor

Synchro nizedBuff er Libera o bloqueio sobre os dados compartilhados .java

(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 );

Synchro espera at que o nizedBuff buffer contenha os dados a ler er .java

(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

que ele pode buffer

Synchro nizedBuff Libera o bloqueio sobre oser dados compartilhados .java

// 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)

101 } // fim do mtodo displayState 102 } // fim da classe SynchronizedBuffer

19

Você também pode gostar