quando readerCount é atualizado. O semáforo db funciona como um semáforo de exclusão mútua para os es-
critores. Também é utilizado pelos leitores para evitar que os escritores entrem no banco de dados enquanto
este estiver sendo lido. O primeiro leitor realiza uma operação P( ) em db, evitando, assim, que algum escri-
tor entre no banco de dados. O leitor final realiza uma operação V ( ) em db. Observe que, se um escritor esti-
ver ativo no banco de dados e n leitores estiverem esperando, então um leitor será colocado na fila em db, e n
- 1 leitores serão colocados na fila em mutex. Observe também que, quando um escritor executa db.V( ), é
possível retomar a execução dos leitores em espera ou de um único escritor em espera. A selcção é feita pelo
escalonador.
138 • Sistemas Operacionais
public class Writer extends Thread
1
public W r i t e r ( i n t w, Database db) { writerNum • w;
server • db;
)
public voíd run( ) { while ( t r u e ) {
System.out.println(Mwríter • + writerNum + " is s l e e p i n g . " ) ;
Database.napping( );
System.out.println("writer " + writerNum • " wants to w r i t e . ' ) ;
server.startWrite( ) ;
// você tem acesso de e s c r i t a no banco de dados System.out.println("writer " + writerNum • " is w r i t i n g . " ) ; Database.napptng( ); server.endWrite( ): SyStem.out.println{"writer * + writerNum • " is done w r i t i n g . " ) ;
í
I
p r i v a t e Database server; p r i v a t e int writerNum; ) F i g u r a 7.20 U m escritor. public class Database1
public Databasef ) ( readerCoimt ° 0;
mutex = new Semaphore(l); db • new Semaphore(l); J
/ / o s l e i t o r e s e escritores chamam este método para " c o c h i l a r " public s t a t i c void nappingf ) {
i n t sleepTime » (int){NAP TIME " Hath.random( ) ) ; try { Thread.sleep(sleepTime*1000); } catch(InterruptedException e) { ) ) public i n t startRead( ) { / / F i g u r a 7.22 ) public i n t endRead( ) ( / / F i g u r a 7.22 ) public void s t a r t W r i t e í ) { / / F i g u r a 7.23 J F i g u r a 7.21 O h a n c o de d a d o s para o p r o b l e m a d o s leitores-cscritores.
Sincronização de Processos • 139
public void endWrite( ) ( / / F i g u r a 7.23
p r i v a t e s t a t i c f i n a l i n t NUM_OF_READERS - 3; p r i v a t e s t a t i c f i n a l i n t NUM~OF_WRITERS = 2; private s t a t i c f i n a l i n t NAP TIME - 15;
í
p r i v a t e i n t readerCount; p r i v a t e Semaphore mutex; p r i v a t e Semaphore db; Figura 7.21 Continuação public i n t startReadí ) { mutex.P( ); ••readerCount;/ / s ç sou o primeiro l e i t o r , informe todos / / o s outros que o banco de dados está sendo l i d o if (readerCount •• 1) db.P( ) ; mutex.V( ); return readerCount;
J
public i n t endRead( ) { mutex.P( ): —readerCount;/ / $ e sou o último l e i t o r , informe todos
/ / o s outros que o banco de dados não está mais sendo l i d o if (readerCount -= 0)
db.V( ) ;
mutex.V( );
return readerCount; I
Figura 7.22 Métodos chamados pelos leitores.
7.6.3 O problema do jantar dos filósofos
Considere cinco filósofos que passam suas vidas meditando e comendo. Os filósofos compartilham uma mesa redonda comum cercada por cinco cadeiras, cada qual pertencente a um filósofo. No centro da mesa está uma tigela de arroz, e a mesa está posta com cinco pauzinhos (hashi) (Figura 7.24). Quando um filósofo medita, não interage com seus colegas. De vez em quando, um dos filósofos fica com fome c tenta pegar os dois pauzinhos mais próximos de si (os pauzinhos q u e estão entre ele e seus colegas da esquerda e direita). Um filósofo só pode pegar um pauzinho de cada vez. Obviamente, n i o poderá pegar um que já esteja na mão de um colega. Quando o filósofo com fome está de posse dos dois pauzinhos ao mesmo tempo, ele poderá co-
mer sem soltá-los. Quando termina de comer, o filósofo solta os pauzinhos e volta a meditar.
O problema do jantar dos filósofos é considerado um problema clássico de sincronização, não por causa de sua importância prática, nem porque os cientistas da computação não gostam de filósofos, mas porque ele
é um exemplo de uma vasta classe de problemas de controle de concorrência. E uma representação simples
140 • Sistemas Operacionais
publíc void startWrtteí ) { db.P( );
I
public void endWrite( ) ( db.V( ) ;
)
Figura 7.23 Métodos chamados pelos escritores.
Figura 7.24 O jantar dos filósofos.
Uma solução simples consiste em representar cada pauzinho por um semáforo. Um filósofo tenta agarrar o pauzinho executando uma operação P naquele semáforo; o filósofo solta o pauzinho executando a opera- ção V nos semáforos apropriados. Assim, os dados compartilhados são:
Semaphore chopStick[ ] • new Semaphore[5];
em que todos os elementos de chopStick são inicializadoscomo 1. A estrutura do f i l ó s o f o / é apresentada na Figura 7.25.
whtle(tnie) (
//pega o pauzinho da esquerda chop$tick[i).P( );
//pega o pauzinho da direita chopStick[(i + 1) % 5].P( ); eating( );
//devolve o pauzinho da esquerda chopStick[i].V( );
//devolve o pauzinho da direita chopStick[(i • 1) H 5].V( ); thinkingí );
I
Figura 7.25 A estrutura do filósofo /'.
Embora essa solução garanta que dois filósofos vizinhos não estarão comendo simultaneamente, ela deve ser rejeitada porque tem a possibilidade de criar um deadlock. Vamos supor que todos os cinco filósofos f i - quem com fome ao mesmo tempo, e cada um pegue o pauzinho à sua esquerda. Todos os elementos de chopS- t l c k agora serão iguais a 0. Quando cada filósofo tentar pegar o pauzinho à sua direita, ficará esperando para sempre.
Várias soluções possíveis para o impasse são listadas a seguir. Essas soluções evitam o deadlock, impondo restrições aos filósofos:
Sincronização de Processos » 141