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


Introduzione ai Thread in Java: Concetti, Implementazione e Sincronizzazione, Appunti di Programmazione Java

Riassunto di sincronizzazione tra piu processi in java

Tipologia: Appunti

2018/2019

Caricato il 29/08/2019

francesco-pistorio
francesco-pistorio 🇮🇹

4.8

(8)

17 documenti

1 / 44

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
THREAD IN JAVA
Come si può realizzare il concetto di Thread in
Java?
NEL MODO PIÙ NATURALE! Sono oggetti
particolari ai quali si richiede un servizio
(chiamato start()) corrispondente al lancio di
una attività, di un thread! MA: non si aspetta che
il servizio termini, esso procede in concorrenza a
chi lo ha richiesto!
Normale Richiesta di
Servizio
Richiesta di Servizio start() a
un Thread
Flusso di
Esecuzione
Esecuzione
del Metodo
X in B
B.X()
return
Attesa che
Termini la
Esecuzione
di B.X()
Oggetto A
Oggetto B
Da qui in poi
ci sono due
flussi di
esecuzione,
l'oggetto A
non aspetta
che termini
l'esecuzione
dell'oggetto B
Esecuzione
del Metodo
run() in B
B.st art()
Oggetto A
Oggetto
Thread B
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c

Anteprima parziale del testo

Scarica Introduzione ai Thread in Java: Concetti, Implementazione e Sincronizzazione e più Appunti in PDF di Programmazione Java solo su Docsity!

THREAD IN JAVA

Come si può realizzare il concetto di Thread in Java?

NEL MODO PIÙ NATURALE! Sono oggetti particolari ai quali si richiede un servizio (chiamato start()) corrispondente al lancio di una attività, di un thread! MA : non si aspetta che il servizio termini, esso procede in concorrenza a chi lo ha richiesto!

Normale Richiesta di Servizio

Richiesta di Servizio start() a un Thread

Flusso di Esecuzione

Esecuzione del Metodo X in B

B.X()

return

Attesa che Termini la Esecuzione di B.X()

Oggetto A

Oggetto B Da qui in poi ci sono due esecuzione,flussi di l'oggetto A non aspetta che termini l'esecuzione dell'oggetto B

Esecuzione del Metodo run() in B

B.start()

Oggetto A

Oggetto Thread B

Thread

Un thread ( lightweight process ) è un singolo flusso sequenziale di controllo all’interno di un processo

Un thread :

  • esegue all’interno del contesto di esecuzione di un unico processo/programma
  • NON ha uno spazio di indirizzamento riservato: tutti i thread appartenenti allo stesso processo condividono lo stesso spazio di indirizzamento
  • ha execution stack e program counter privati

Esempio di classe SimpleThread che è

sottoclasse di Thread (modalità 1):

public class SimpleThread extends Thread { public SimpleThread(String str) { super(str); } public void run () { for (int i = 0; i < 10; i++) { System.out.println(i +" "+ getName()); try { sleep((int)(Math.random()*1000)); } catch (InterruptedException e){} } System.out.println("DONE! " +getName()); } }

public class TwoThreadsTest { public static void main (String[] args) { new SimpleThread("Jamaica"). start (); new SimpleThread("Fiji"). start (); } }

E se occorre definire thread che non siano necessariamente sottoclassi di Thread?

2) come classe che implementa

l’interfaccia runnable

  • implementare il metodo run() nella

classe

  • creare un’istanza della classe tramite new
  • creare un’istanza della classe Thread

con un’altra new , passando come parametro l’istanza della classe che si è creata

  • invocare il metodo start() sul thread

creato, producendo la chiamata al suo metodo run()

Interfaccia Runnable:

maggiore flessibilità derivante dal poter essere sottoclasse di qualsiasi altra classe

Il ciclo di vita di un thread

  • Creato subito dopo l’istruzione new le variabili sono state allocate e inizializzate; il thread è in attesa di passare allo stato di eseguibile
  • Runnable thread è in esecuzione, o in coda d’attesa per ottenere l’utilizzo della CPU
  • Not Runnable il thread non può essere messo in esecuzione dallo scheduler. Entra in questo stato quando in attesa di un’operazione di I/O, o dopo l’invocazione dei metodi suspend() , wait() , sleep()
  • Dead al termine “naturale” della sua esecuzione o dopo l’invocazione del suo metodo stop() da parte di un altro thread

Metodi per il controllo di thread

start() fa partire l’esecuzione di un

thread. La macchina virtuale Java invoca il metodo run() del thread appena creato

stop() forza la terminazione

dell’esecuzione di un thread. Tutte le risorse utilizzate dal thread vengono immediatamente liberate (lock inclusi), come effetto della propagazione dell’eccezione ThreadDeath

suspend() blocca l'esecuzione di un thread in attesa di una successiva operazione di resume. Non libera le risorse impegnate dal thread (possibilità di deadlock )

resume() riprende l'esecuzione di un

thread precedentemente sospeso. Se il thread riattivato ha una priorità maggiore di quello correntemente in esecuzione, avrà subito accesso alla CPU, altrimenti andrà in coda d'attesa

Il problema di stop()e suspend()

stop() e suspend() rappresentano azioni “brutali” sul ciclo di vita di un thread => rischio di determinare situazioni di blocco critico ( deadlock )

Infatti:

  • se il thread sospeso aveva acquisito una risorsa in maniera esclusiva , tale risorsa rimane bloccata e non è utilizzabile da altri, perché il thread sospeso non ha avuto modo di rilasciare il lock su di essa
  • se il thread interrotto stava compiendo un insieme di operazioni su risorse comuni, da eseguirsi idealmente in maniera atomica , l’interruzione può condurre ad uno stato inconsistente del sistema

Î JDK 1.2, pur supportandoli ancora per ragioni di back-compatibility , sconsiglia l’utilizzo dei metodi stop(), suspend() e resume() ( metodi deprecated )

Si consiglia invece di realizzare tutte le azioni di controllo e sincronizzazione fra thread tramite i metodi wait() e notify() su variabili condizione (astrazione di monitor )

Priorità dei thread in Java

Scheduling : esecuzione di una molteplicità di thread su una singola CPU, in un qualche ordine

Macchina virtuale Java (JVM) Fixed Priority Scheduling : algoritmo di scheduling molto semplice e deterministico

  • JVM sceglie il thread in stato runnable con priorità più alta
  • Se più thread in attesa di eseguire hanno uguale priorità , la scelta della JVM avviene con una modalità di tipo round- robin.

La classe Thread fornisce i metodi:

  • setPriority(int num)
  • getPriority()

con valori di num compresi fra

MIN_PRIORITY e MAX_PRIORITY (costanti

definite anch’esse nella classe Thread)

SINCRONIZZAZIONE di THREADS

Quando due o più thread eseguono concorrentemente, è in generale impossibile prevedere l'ordine in cui le loro istruzioni verranno eseguite.

Problemi nel caso in cui i thread invocano metodi sullo stesso oggetto di cui condividono il riferimento. SONO POSSIBILI INCONSISTENZE!

Esempio: Oggetto conto_corrente con metodo versamento(importo) public void versamento(int importo) { int nuovo_totale; //variabile locale al metodo nuovo_totale = totale_conto + importo - tasse; //totale_conto è una variabile dell'oggetto //e indica i soldi totali sul conto //l'istruzione calcola il nuovo totale del //conto corrente mettendo il risultato nella //variabile locale totale_conto = nuovo_totale; //metto il totale calcolato nella variabile //dell'oggetto che memorizza il conto totale } Supponiamo che due thread abbiano entrambi un riferimento all'oggetto e invochino separatamente il metodo versamento per fare ognuno un versamento sul conto…

ESEMPIO DI INCONSISTENZA

Supponiamo che l'oggetto conto_corrente abbia nella sua variabile totale_conto il valore 2000. Ci si aspetta che se qualcuno deposita 1000 e qualcun altro deposita 500 , e supponendo che le tasse per ogni versamento siano 100 , alla fine la variabile totale_conto valga 3300. Supponiamo che questi due depositi vengano fatti in concorrenza da due thread diversi e che durante l'esecuzione i thread si alternino sul processore, come segue Thread A Thread B

conto_corrente.versamento(1000) // il thread invoca il metodo e il flusso di esecuzione fa a eseguire le istruzioni di tale metodo

nuovo_totale= totale_conto+importo-tasse; //nuovo_totale = 2000+1000-

totale_conto = nuovo_totale; //totale_conto vale 2900

conto_corrente.versamento(500) // il thread invoca il metodo e il flusso di esecuzione fa a eseguire le istruzioni di tale metodo

nuovo_totale= totale_conto+importo-tasse; //nuovo_totale = 2000+500-

totale_conto = nuovo_totale; //totale_conto vale 2400

Alla fine, totale_conto vale 2400!!!! Il metodo versamento() non è atomico

In pratica: ‰ a ogni oggetto Java è automaticamente associato un lock ‰ per accedere a un metodo o una sezione synchronized, un thread deve prima acquisire il lock dell’oggetto ‰ il lock è automaticamente rilasciato quando il thread esce dalla sezione synchronized, o se viene interrotto da un’eccezione ‰ un thread che non riesce ad acquisire un lock rimane sospeso sulla richiesta della risorsa fino a che il lock non è disponibile

Quando un thread in esecuzione tenta di accedere ad una sezione synchronized di un oggetto il cui lock è già stato acquisito da un altro thread, che esegue concorrentemente, esso si mette in attesa di poter acquisire il lock. Il thread rimane in stato runnable. Appena il lock è rilasciato, lo scheduler può potenzialmente mettere in esecuzione il thread.

NOTA: ad ogni oggetto contenente metodi o blocchi synchronized viene assegnata una sola variabile condizione Î Due thread non possono accedere contempo- raneamente a due sezioni synchronized diverse di uno stesso oggetto

ESEMPIO – Lettura Dati da Socket .... public class AsyncReadSocket extends Thread { private Socket s; private StringBuffer result; public AsyncReadSocket(Socket s) { this.s = s; result = new StringBuffer(); } public void run() { DataInputStream is = null; try { is=new DataInputStream(s.getInputStream()); } catch (Exception e) {} while (true) { try { char c = is.readChar(); result.append(c); } catch (Exception e) {} } } public String getResult() { //metodo usato da un altro thread per leggere //la stringa letta dalla socket String retval = result.toString(); result = new StringBuffer(); return retval; } }

ESEMPIO MODIFICATO .... public class AsyncReadSocket extends Thread { private Socket s; private StringBuffer result; public AsyncReadSocket(Socket s) { this.s = s; result = new StringBuffer(); } public void run() { DataInputStream is = null; try { is=new DataInputStream(s.getInputStream()); }catch(Exception e){} while (true) { try{ char c = is.readChar(); appendResult(c); }catch(Exception e){} } } public synchronized String getResult() { String retval = result.toString(); Result = new StringBuffer(); return retval; } public synchronized void appendResult(char c) { result.append(c); }}

  1. se avessimo definito come sychronized solo getResult() e non appendResult()?

=> non avremmo risolto i problemi di corsa critica

  1. e se definissimo run() come metodo synchronized?

La mutua esclusione durerebbe all’infinito e due Thread di tipo AsynchReadSocket non potrebbero eseguire concorrentemente