Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Sincronizzazione tra Thread in Java: Esempio Produttore-Consumatore, Dispense di Informatica

Spiegazione sulla programmazione concorrente

Tipologia: Dispense

2019/2020

In vendita dal 02/10/2020

Lunetta19
Lunetta19 🇮🇹

5

(1)

5 documenti

1 / 4

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
PROGRAMMAZIONE CONCORRENTE, DA PAG 84 A PAG 96
COMUNICAZIONI TRA THREAD
La programmazione java permette la comunicazione tra thread. Un thread può comunicare agli altri
thread che un evento si è verificato e rendere in questo modo efficiente la sincronizzazione tra i
thread (si può così evitare l'inefficiente utilizzo del polling, ovvero la continua interrogazione dello
stato di una variabile per capire se un evento si è verificato, cosa che impegna la CPU a vuoto
finchè non si verifica l'evento).
La comunicazione tra thread in java si basa su 3 metodi:
wait ( ): fa uscire il thread dal monitor (che in java è implicito, non visibile e realizzato
semplicemente anteponendo la keyword synchronized ai metodi che sono sezioni critiche
del programma) e lo sospende finchè un altro metodo entra nel monitor ed esegue il metodo
notify ( )
notify ( ); attiva il primo thread che ha eseguito una wait sullo stesso oggetto
notify ( ) all: attiva tutti ithread che hanno eseguito un thread sullo stesso oggetto. Il thread
con la priorità più alta viene mandato in esecuzione per primo.
ESEMPIO PRODUTTORE-CONSUMATORE
Scrivere un programma che utilizza la sincronizzazione tra thread in cui il produttore mette (scrive) un
dato in una locazione di memoria (buffer) e il consumatore lo prende (legge). Si deve garantire il
corretto alternarsi di scrittura e lettura per evitare duplicazioni o perdite di dati.
Per ottenere ciò scriviamo una classe Buffer con un attributo (unDato: il dato che viene scambiato)
e due metodi, che chiamiamo metti( ) e prendi( ). I due metodi scrivono e leggono un solo dato
alla volta. Essi devono essere sincronizzati (sincronyzed) in modo da non poter essere interrotti da
nessun altro thread quando scrivono o leggono. Inoltre, occorre che i due metodi:
notifichino (notify) l'avvenuta scrittura o lettura in modo da sincronizzare produttore e
consumatore.
Si sospendano (wait) e non scrivono o leggono un altro dato finché non arriva la notifica
relativa rispettivamente alla lettura del dato precedente o alla scrittura del nuovo dato.
La classe Produttore è costituita da un costruttore che usa un oggetto della classe Buffer ( che
chiamiamo buffer), genera un thread figlio ed invoca il metodo metti ( ) che scrive un dato
nell'oggetto buffer. L'invocazione è fatta in un ciclo for in modo da scrivere n dati. Per ogni
iterazione (ovvero per ogni scrittura di un dato) il metodo metti ( ) scrive un dato, poi sospende se
stesso (wait) e si riattiva solo quando si verifica il notify sullo stesso oggetto buffer (cosa che farà
il consumatore quando avrà letto il dato)
La classe Consumatore è costituita da un costruttore che usa lo stesso oggetto buffer, genera anche
lui un thread figlio ed invoca il metodo prendi ( ) che legge il dato dall'oggetto buffer. Anche in
questo caso l'invocazione è fatta in un ciclo for in modo da leggere gli n dati che vengono scritti dal
produttore. Per ogni iterazione (ovvero per ogni lettura di un dato) il metodo prendi ( ) legge un
dato, poi sospende se stesso (wait) e si riattiva solo quando si verifica il notify sullo stesso oggetto
buffer (cosa che farà il produttore quando avrà scritto il nuovo dato)
Infine la classe Principale (con il main) dopo aver creato l'oggetto ilBuffer, crea un oggetto
produttore che richiede come parametro di ingresso l’oggetto buffer, e un oggetto consumatore
che richiede anch'esso come parametro d’ingresso lo stesso oggetto buffer. Quando sarà eseguita
questa classe Principale, verranno creati i due oggetti produttore e consumatore che cominceranno
a scambiarsi i dati.
pf3
pf4

Anteprima parziale del testo

Scarica Sincronizzazione tra Thread in Java: Esempio Produttore-Consumatore e più Dispense in PDF di Informatica solo su Docsity!

PROGRAMMAZIONE CONCORRENTE, DA PAG 84 A PAG 96

COMUNICAZIONI TRA THREAD

La programmazione java permette la comunicazione tra thread. Un thread può comunicare agli altri thread che un evento si è verificato e rendere in questo modo efficiente la sincronizzazione tra i thread (si può così evitare l'inefficiente utilizzo del polling , ovvero la continua interrogazione dello stato di una variabile per capire se un evento si è verificato, cosa che impegna la CPU a vuoto finchè non si verifica l'evento). La comunicazione tra thread in java si basa su 3 metodi:

  • wait ( ): fa uscire il thread dal monitor (che in java è implicito, non visibile e realizzato semplicemente anteponendo la keyword synchronized ai metodi che sono sezioni critiche del programma) e lo sospende finchè un altro metodo entra nel monitor ed esegue il metodo notify ( )
  • notify ( ) ; attiva il primo thread che ha eseguito una wait sullo stesso oggetto
  • notify ( ) all: attiva tutti ithread che hanno eseguito un thread sullo stesso oggetto. Il thread con la priorità più alta viene mandato in esecuzione per primo. ESEMPIO PRODUTTORE-CONSUMATORE Scrivere un programma che utilizza la sincronizzazione tra thread in cui il produttore mette (scrive) un dato in una locazione di memoria (buffer) e il consumatore lo prende (legge). Si deve garantire il corretto alternarsi di scrittura e lettura per evitare duplicazioni o perdite di dati. Per ottenere ciò scriviamo una classe Buffer con un attributo (unDato: il dato che viene scambiato ) e due metodi, che chiamiamo metti( ) e prendi( ). I due metodi scrivono e leggono un solo dato alla volta. Essi devono essere sincronizzati ( sincronyzed) in modo da non poter essere interrotti da nessun altro thread quando scrivono o leggono. Inoltre, occorre che i due metodi:
  • notifichino ( notify ) l'avvenuta scrittura o lettura in modo da sincronizzare produttore e consumatore.
  • Si sospendano ( wait) e non scrivono o leggono un altro dato finché non arriva la notifica relativa rispettivamente alla lettura del dato precedente o alla scrittura del nuovo dato. La classe Produttore è costituita da un costruttore che usa un oggetto della classe Buffer ( che chiamiamo buffer) , genera un t hread figlio ed invoca il metodo metti ( ) che scrive un dato nell'oggetto buffer. L'invocazione è fatta in un ciclo for in modo da scrivere n dati. Per ogni iterazione (ovvero per ogni scrittura di un dato) il metodo metti ( ) scrive un dato, poi sospende se stesso ( wait ) e si riattiva solo quando si verifica il notify sullo stesso oggetto buffer (cosa che farà il consumatore quando avrà letto il dato) La classe Consumatore è costituita da un costruttore che usa lo stesso oggetto buffer , genera anche lui un thread figlio ed invoca il metodo prendi ( ) che legge il dato dall'oggetto buffer. Anche in questo caso l'invocazione è fatta in un ciclo for in modo da leggere gli n dati che vengono scritti dal produttore. Per ogni iterazione (ovvero per ogni lettura di un dato) il metodo prendi ( ) legge un dato, poi sospende se stesso ( wait ) e si riattiva solo quando si verifica il notify sullo stesso oggetto buffer (cosa che farà il produttore quando avrà scritto il nuovo dato) Infine la classe Principale (con il main ) dopo aver creato l'oggetto ilBuffer , crea un oggetto produttore che richiede come parametro di ingresso l’oggetto buffer, e un oggetto consumatore che richiede anch'esso come parametro d’ingresso lo stesso oggetto buffer. Quando sarà eseguita questa classe Principale , verranno creati i due oggetti produttore e consumatore che cominceranno a scambiarsi i dati.

Le classi saranno quindi del tipo: Buffer unDato metti ( ) prendi ( ) Produttore buffer Produttore run Consumatore buffer Consumatore run Principale

main Scriviamo adesso le classi: class Buffer { int unDato; boolean datoNelBuffer = false; synchronized void metti (int dato) { if (datoNelBuffer) wait ( ); unDato=dato; datoNelBuffer=true; System.out.println (“Scritto: “ + dato); notify ( ); } synchronized int prendi ( ) { if (! datoNelBuffer) wait(); System.out.println (“\t\t letto: “ + unDato); datoNelBuffer=false; notify( ); return unDato; } } class Produttore implements Runnable { Buffer buffer;

wait ( ); } catch (Exception e) { }; System.out.println (“\t\t letto: “ + unDato); datoNelBuffer=false; notify( ); return unDato; } } Il programma corretamente stampa (a sinistra la colonna di stampe del metodo metti ( ), a destra la colonna delle stampe del metodo prendi ( )): Scritto: 45 letto: 45 Scritto: 46 letto: 46 Scritto: 47 letto: 47 Scritto: 48 letto: 48 Scritto: 49 letto: 49 Scritto: 50 letto: 50 Scritto: 51 letto: 51 Scritto: 52 letto: 52 Scritto: 53 letto: 53 Scritto: 54 letto: 54 Quindi, per implementare una sincronizzazione tra thread:

  • si scrive una classe che descrive la risorsa condivisa, ovvero gli attributi della risorsa e i metodi che saranno usati da chi genera l'evento (produttore) e da che lo deve rilevare (consumatore). I metodi che agiscono sulla risorsa devono essere synchronized e contenere il metodo ◦ wait ( ) in attesa della notifica che l'evento si è verificato ◦ notify ( ) per notificare che l'evento si è verificato
  • si scrivono quindi le classi dei produttori e dei consumatori che contengono un costruttore che riceve come parametro la risorsa condivisa e crea un thread che utilizza gli opportuni metodi della risorsa condivisa.
  • Infine, si scrive la classe main che istanzia la risorsa condivisa, il produttore e il consumatore.