SINCRONIZAÇÃO DE THREADS // Fig. 23.6: Buffer.java // Interface Buffer especifica métodos chamados por Produtor e Consumidor. public interface Buffer { public void set( int value ); // coloca o valor int no Buffer public int get(); // retorna o valor int a partir do Buffer } // fim da interface Buffer -------------------------------------------------------------------------- // Fig. 23.7: Producer.java // O método run do Producer armazena os valores de 1 a 10 no buffer. import java.util.Random; public class Producer implements Runnable { private static Random generator = new Random(); private Buffer sharedLocation; // referência a objeto compartilhado // construtor public Producer( Buffer shared ) { sharedLocation = shared; } // fim do construtor Producer // armazena os valores de 1 a 10 em sharedLocation public void run() { int sum = 0; for ( int count = 1; count <= 10; count++ ) { try // dorme de 0 a 3 segundos, então coloca valor em Buffer { Thread.sleep( generator.nextInt( 3000 ) ); // thread sleep sharedLocation.set( count ); // configura valor no buffer sum += count; // incrementa soma de valores System.out.printf( "\t%2d\n", sum ); } // fim do try // se a thread adormecida é interrompida, imprime rastreamento de pilha catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch } // fim do for System.out.printf( "\n%s\n%s\n", "Producer done producing.", "Terminating Producer." ); } // fim do método run } // fim da classe Producer -------------------------------------------------------------------------------- // Fig. 23.8: Consumer.java // O método run de Consumer itera dez vezes lendo um valor do buffer. import java.util.Random; public class Consumer implements Runnable { private static Random generator = new Random(); private Buffer sharedLocation; // referência a objeto compartilhado // construtor public Consumer( Buffer shared ) { sharedLocation = shared; } // fim do construtor Consumer // lê o valor do sharedLocation quatro vezes e soma os valores public void run() { int sum = 0; 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(); System.out.printf( "\t\t\t%2d\n", sum ); } // fim do try // se a thread adormecida é interrompida, imprime rastreamento de pilha catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch } // fim do for System.out.printf( "\n%s %d.\n%s\n", "Consumer read values totaling", sum, "Terminating Consumer." ); } // fim do método run } // fim da classe Consumer --------------------------------------------------------------------------------- RELACIONAMENTO PRODUTOR-CONSUMIDOR SEM SINCRONIZAÇÃO // Fig. 23.9: UnsynchronizedBuffer.java // UnsynchronizedBuffer representa um único inteiro compartilhado. public class UnsynchronizedBuffer implements Buffer { private int buffer = -1; // compartilhado pelas threads Produtor e Consumidor // coloca o valor no buffer public void set( int value ) { System.out.printf( "Producer writes\t%2d", value ); buffer = value; } // fim do método set // retorna valor do buffer public int get() { System.out.printf( "Consumer reads\t%2d", buffer ); return buffer; } // fim do método get } // fim da classe UnsynchronizedBuffer ---------------------------------------------------------------------------------- // Fig. 23.10: SharedBufferTest.java // Aplicativo mostra duas threads que manipulam um buffer não-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(); System.out.println( "Action\t\tValue\tProduced\tConsumed" ); System.out.println( "------\t\t-----\t--------\t--------\n" ); // tenta iniciar as threads produtora e consumidora fornecendo acesso a cada uma // a sharedLocation try { application.execute( new Producer( sharedLocation ) ); application.execute( new Consumer( sharedLocation ) ); } // fim do try catch ( Exception exception ) { exception.printStackTrace(); } // fim do catch application.shutdown(); // termina aplicativo quando as threads terminam } // fim do main } // fim da classe SharedBufferTest --------------------------------------------------------------------------------------- RELACIONAMENTO PRODUTOR-CONSUMIDOR COM SINCRONIZAÇÃO (IMPLMENTANDO MONITOR) // Fig. 23.19: SynchronizedBuffer.java // SynchronizedBuffer sincroniza acesso a um único inteiro compartilhado. public class SynchronizedBuffer implements Buffer { private int buffer = -1; // compartilhado pelas threads Produtor e Consumidor private boolean occupied = false; // contagem de buffers ocupados // coloca o valor no buffer public synchronized void set( int value ) { // enquanto não houver posições vazias, coloca a thread em estado de espera while ( occupied ) { // envia informações de thread e de buffer para a saída, então espera try { System.out.println( "Producer tries to write." ); displayState( "Buffer full. Producer waits." ); wait(); } // fim do try catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch } // fim do while buffer = value; // configura novo valor de buffer // indica que a produtora não pode armazenar outro valor // até a consumidora recuperar valor atual de buffer occupied = true; displayState( "Producer writes " + buffer ); notify(); // instrui a thread em espera a entrar no estado executável } // fim do método set; libera bloqueio em SynchronizedBuffer // retorna valor do buffer public synchronized int get() { // enquanto os dados não são lidos, coloca thread em estado de espera while ( !occupied ) { // envia informações de thread e de buffer para a saída, então espera try { System.out.println( "Consumer tries to read." ); displayState( "Buffer empty. Consumer waits." ); wait(); } // fim do try catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch } // fim do while // indica que a produtora pode armazenar outro valor // porque a consumidora acabou de recuperar o valor do buffer occupied = false; int readValue = buffer; // armazena valor em buffer displayState( "Consumer reads " + readValue ); notify(); // instrui a thread em espera a entrar no estado executável return readValue; } // fim do método get; libera bloqueio em SynchronizedBuffer // exibe a operação atual e o estado de buffer public void displayState( String operation ) { System.out.printf( "%-40s%d\t\t%b\n\n", operation, buffer, occupied ); } // fim do método displayState } // fim da classe SynchronizedBuffer ----------------------------------------------------------------------------------- // Fig. 23.20: SharedBufferTest2.java // Aplicativo mostra duas threads que manipulam um buffer sincronizado. import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SharedBufferTest2 { public static void main( String[] args ) { // cria novo pool de threads com duas threads ExecutorService application = Executors.newFixedThreadPool( 2 ); // cria SynchronizedBuffer para armazenar ints Buffer sharedLocation = new SynchronizedBuffer(); System.out.printf( "%-40s%s\t\t%s\n%-40s%s\n\n", "Operation", "Buffer", "Occupied", "---------", "------\t\t--------" ); try // tenta iniciar a produtora e a consumidora { application.execute( new Producer( sharedLocation ) ); application.execute( new Consumer( sharedLocation ) ); } // fim do try catch ( Exception exception ) { exception.printStackTrace(); } // fim do catch application.shutdown(); } // fim de main } // fim da classe SharedBufferTest2 -----------------------------------------------------------------------------------