Introduzione
mercoledì 21 febbraio 2018 14:11
Motivazione principali per scivere programmi concorrenti
Tante cose da fare e processore abbastanza potente
- ▪ Concorrenza virtuale
▪ Processi cooperanti
Per modellare parallelismo del mondo reale
- Quando si hanno più processori (sfrutta parallelismo reale)
-
Sequenziale vs Concorrente
Batch processing: dati di input disponibili preventivamente
- Programmi inter(merda)attivi: periodi in cui programma aspetta che utente fornisca un dato per
- elaborazione
Parallelismo nel mondo reale (fare tante cose contemporaneamente)
Legge di Moore: capacità dei calcolatori aumenta ogni 18 mesi
Processi concorrenti devono programmare e coordinare le loro azioni
Possono portare a nuova condizioni di errore
-
Problemi
Deadlock (ciascuna attività concorrente è in attesa del risultato di un'altra attività per eseguire)
- Race condition (due o più attività simultanee tentano di aggiornare lo stesso oggetto)
- Starvation (a una o più attività simultanee vengono continuamente negate risorse)
-
Proprietà desiderate
Safety (attività concorrenti non interferiscono tra di loro col rischio di corrompere i dati)
- Liveness (attività concorrenti devono fare progressi con le loro attività senza deadlock o starvation)
-
Programmazione in Ambiente Distribuito
Componenti che girano contemporaneamente su più computer
- Utilizzati socket
- Remote Method Invocation (RMI)
- ○ Implementa RPC
○ Client può invocare un metodo su un oggetto remoto in modo trasparente
Organizzazione Client Server
Server (entità che mette a disposizione delle risorse o servizi)
- Client (entità che richiede risorse e servizi al server)
- Richieste e risposte seguono un protocollo (insieme di regole)
-
Programmazione a Eventi
Per la gestione di interfacce a oggetti
Programmazione Concorrente e Distribuita Page 1
Gli Stream di I/O
mercoledì 21 febbraio 2018 16:03
Gestioni di ingresso e uscita, definisce le operazioni il pacchetto java.io
Stream (Flussi): sequenze ordinate di dati, hanno una sorgente o una destinazione
Flussi di Input/Output
Per ricevere dei dati, una app apre lo stream colleggato a una sorgente da cui legge le info
Sorgente può essere in memoria, disco o remota
-
Pacchetto composto da due sezioni:
Flussi di Byte
- ○ Unità di informazione è il byte
○ I/O binario (dati tutti in formato binario)
○ Classi indicate come:
▪ Stream di ingresso
▪ Stream di uscita
Flussi di Caratteri
- ○ Unità di informazione sono caratteri Unicode
○ I/O testuale
○ Classi indicate come:
▪ Lettori
▪ Scrittori
Errori
Se operazioni di ingresso o uscita non vanno a buon fine
Segnalati in due modi:
Cambiando lo stato dello stream
- Lanciando un'eccezione di tipo IOException
-
Ingresso di File Binari
Si utilizza FileInputStream [ FileInputStream in = new FileInputStream(nomeFile); ]
Per leggere un byte si usa il metodo read() che restituisce int con byte letto
Quando non c'è più nulla da leggere read restituisce -1
Scrittura su File Binari
Si utilizza FileOutputStream [ FileOutputStream out = new FileOutputStream(nomeFIle) ]
Per scrivere un byte si usa il metodo write(int c)
Lettura di un File di testo
Si utilizza FileReader Programmazione Concorrente e Distribuita Page 2
Thread e Multi-Thread
lunedì 26 febbraio 2018 11:10
Programma è insieme di istruzioni di alto livello o in linguaggio macchina
Processo è programma in esecuzione (possibile + processi in parallelo grazie a scheduler)
Differenze tra Processi e Thread
Se i processi condividono lo stesso spazio degli indirizzi --> processi leggeri o thread
Se i processi hanno il proprio spazio degli inidrizzi ---------> processi pesanti o processi
Programmazione concorrente: pratica di implementare programmi che contengono più flussi di
esecuzione (thread), creando thread per ogni richiesta
Programmi Concorrenti e Sequenziali
Sequenziale è deterministico
Modello di Memoria Semplificato (SMM)
Utilizza diversi tipi di memoria:
Heap
- Method area
- Program context
-
Sleeping and Blocking
Spesso un thread può essere sospeso per un certo tempo, assume lo stato di sleeping o blocked.
Resta in questo stato per il tempo precisato poi torna ready
Per far partire i thread si utilizza start
Per far si che dopo una start inizi ad eseguire il task t, mandiamo in sleep il metodo main (esempio
slide pag 60)
Sleep()
Mette in attesa un processo senza consumare cicli del processore
È possibile interrompere uno sleep via interrupt, quindi sleep va inserito in un try catch
isAlive()
Testare se il metodo è vivo, il thread viene considerato alive finchè il metodo run() non ritorna
Join()
Attende la terminazione del thread sul quale è richiamato
Per fermare un thread si usa un' interrupt
Usando il flag dell'interrupt e sollevando l'eccezione InterruptException; dovrebbe quindi terminare
l'esecuzione
Yield()
Pemette ad un altro thread di lasciare volontariamente il processore ad un altro thread
Si usa quando non vi è preemption
Programmazione Concorrente e Distribuita Page 3
01 - Thread e MultiThread
lunedì 23 aprile 2018 14:11
Thread e Multi-Thread
Processi leggeri o Thread --> processi condividono lo stesso spazio degli indirizzi
Processi pesanti o processi --> processi hanno il proprio spazio negli indirizzi
Ogni programma in esecuzione è un thread, il main è associato al thread main.
Programmazione concorrente = pratica di implementare programmi che contengano più flussi di
esecuzione (Thread)
Creare un thread
1. Estendere la classe java.lang.Thread
2. Riscrivere (Override) il metodo run()
3. Creare istanza della sottoclassi
4. Richiamare il metodo start() su questa istanza
Limitazione: le classi che estendono Thread non possono estendere altre classi
Il metodo run()
Costituisce l'entry point del thread
- ○ Thread ALIVE --> finchè run() non ritorna
○ Thread DEAD --> quando run() ritorna
Dopo che un thread è DEAD non può essere rieseguito, se no si deve creare una nuova istanza
- Non si può far partire lo stesso thread più volte
-
Approccio alternativo alla creazione di un thread
Si possono creare thread usanda java.lang.Runnable
1. Definire implementazione Runnable
2. Realizzare il metodo run()
3. Creare istanza
4. Instanziare un nuovo Thread passando al costruttore l'istanza che implementa Runnable
5. Richiamare start() su Thread
Programmi concorrenti e sequenziali
Programmi concorrenti hanno proprietà molto diverse dai programmi sequenziali: quest' ultimo
riproduce lo stesso input ogni volta, invece nei programmi concorrenti il comportamento di un
thread dipende da un altro thread
Il metodo run() può essere chiamato più volte
Il metodo start() può essere chiamato solo una volta
Flusso di Controllo
Se ci sono due flussi di esecuzione e l'oggetto A non aspetta che termini l'esecuzione dell' oggetto B,
il programma termina quando tutti i thread non daemon terminano. Un thread daemon fornisce un
servizio generale in background mentre il programma esegue altro.
Thread Daemon
Particolare tipo di thread con:
Priorità molto bassa (eseguiti quando nessun altro thread è in esecuzione)
- Utilizzati come fornitori di servizi per i thread normali
-
JVM termina il programma terminando i thread daemon quando sono gli unici in esecuzione
Stati di un Thread
Born, Ready, Running, Dead
Invochiamo il metodo start -> thread diventa ready
- Quando viene selezionato dallo scheduler diventa Running ed esegue run()
- Programmazione Concorrente e Distribuita Page 4
Quando viene selezionato dallo scheduler diventa Running ed esegue run()
-
Java Thread Scheduling
Come funziona lo scheduler?
JVM schedula l'esecuzione dei thread utilizzando un algoritmo di scheduling preempitive e
- priority based
Tutti i thread hanno una priorità e il thread con la priorità più alta tra quelli ready viene
- schedulato per essere eseguito
Con il diritto di preemption lo scheduler sottrae la CPU al processo e assegna al nuovo
- processo
Politica di Scheduler
Java non specifica il tipo di politica, dipende dal Sistema Operativo
Se i due thread hanno la stessa priorità:
In presenza di scheduling non preemptive eseguirà solo il thread lanciato per primo
- Se vanno entrambi, lo scheduling è preemptive
- ○ Quando scade il quanto di tempo al thread, viene sottratta CPU e passata al thread
Modello di Memoria Semplificato (SMM)
Utilizzato dalla Macchina Virtuale Semplificata (SVM) utilizza diversi tipi di memoria:
Heap (memorizzare oggetti e loro dati)
- Method Area (definizioni della classi e istruzioni compilate)
- Program Context (informazioni uniche per thread come stack e PC)
-
SMM durante l'esecuzione di un Programma Single Thread
SVM crea heap e il thread context, method area e carica i metodi
- Crea il PC con valore non definito
- SVM inserisce un activation record per il main nello stack
- SVM assegna la linea di codice dove si istanzia la classe (PCexe n = new PCexe) al PC e al thread
- PC
SVM esegue la riga 10 - crea istanza della classe Pcexe nello heap
- SVM aggiorna il PC che passa a riga seguente
- SVM chiama run()
- SVM crea nuovo activation record per il run() contente la variabile locale counter
- Aggiorna il pc che diventa 4
- *Step 5-6-7* SVM esegue righe da 4 a 6 comprese (stampa variabili, incrementa e stampa)
- PC punta alla linea 7, counter vale 1
- SVM esegue return (elimina activation record di run dallo stack)
- Main esegue il return e elimina activation record del metodo main
-
SVM Thread States
Un programma concorrente ha più thread, quindi la SVM deve scegliere quale mandare in
esecuzione, la decisione dipende da vari fattori.
Programmazione Concorrente e Distribuita Page 5
Sleeping and Blocking
Un thread può essere sospeso per un certo periodo di tempo (Thread.sleep)
In questo caso, assume lo stato di sleeping o blocked
Sleep()
Non utilizza cicli del processore
- Metodo statico e mette in pausa il thread corrente
- Mentre è in sleep può essere interrotto da un thread o sollevata un eccezione
- ▪ Interrupt dovrebbe essere usata con wait, non con sleep
Creare un metodo che rallenti l'esecuzione di due thread non è una buona soluzione, si
- sprecano cicli di processore, inoltre si può avere ritardo rispetto al momento desiderato
(Busy Loop())
isAlive()
Testare se è vivo
- Quando viene chiamato start() il thread è alive
- Thread considerato alive finche non run() non ritorna
-
Join() Attende la terminazione del thread sul quale è chiamato
- Thread sul quale è chiamato join rimane bloccato in attesa che termini l'altro
-
Come stoppare un thread?
Non vi è un modo sicuro ed efficace per fermare un thread, esistevano vari metodi ma sono
- stati deprecati
Per fermare un thread la risposta definitiva è interrupt()
-
Interrupt()
Setta un flag di interruzione nel thread di destinazione e ritorna
- Thread non viene effettivamente interrotto
- Thread può controllare se flag è settato e nel caso uscire
- Metodi che mettono in pausa controllano il flag di interruzione prima dopo e durante
- Se flag è settato lancia un eccezione
- Thread da interrompere intercetta l'eccezione e dovrebbe terminare l'esecuzione
-
Problemi con Interrupt()
Interrupt() non funziona se il thread non esegue mai metodi di attesa (sleep, join)
- Thread devono cooperare per verificare il suo stato di interruzione
- ▪ isInterrupted(): controlla il flag senza resettarlo
▪ Thread.interrupted(): controlla il flag e se è settato lo resetta
Thread.yield()
Permette ad un thread di lasciare volontariamente il processore ad un altro thread
- Utile nel caso di un thread che non esegue spesso operazioni che lo mettano in attesa
- Si cede il contro allo scheduler che scegle un altro thread da mandare in esecuzione
- Quando si usa yield()?
- ▪ Quando non c'è preemption
▪ In casi particolari
Programmazione Concorrente e Distribuita Page 6
02 - Correctness
lunedì 7 maggio 2018 14:37
Il numero di diversi possibili percorsi di esecuzione di un programma concorrente è definito come:
NON-DETERMINISMO.
Il non-determinismo implica che testare un programma concorrente è solitamente difficile dato che
non è possibile determinare l'ordine assoluto dell'esecuzione delle istruzioni.
Questi problemi sono definiti come Race Condition: tutte quelle situazioni in cui thread diversi
operano su una risorsa comune ed in cui il risultato viene a dipendere dall'ordine in cui essi
effettuano le loro operazioni
Le Race Condition si possono verificare in due condizioni:
1. Una risorsa deve essere condivisa tra due thread
2. Deve esistere almeno un percorso di esecuzione tra i thread in cui una risorsa è condivisa in
modo non sicuro
Per dimostrare la correttezza di un programma concorrente esistono due modi:
• Dimostrare p
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Programmazione concorrente e distribuita
-
Programmazione Distribuita
-
Programmazione Distribuita - Serverless Computing
-
Programmazione