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.
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.
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.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
Processi e Tabelle dei Threads
Processi, e quindi esistono tante tabelle dei Threads quanti sono i Processi che generano Threads, nella Gestione a livello Kernel è presente un'unica Tabella dei Threads condivisa da tutti. Esiste anche una Gestione Thread Ibrida nel quale il programmatore può decidere quali sono i Thread che il SO deve vedere e quali no, definendo separatamente il numero N di Threads a livello Utente associati ad un Processo ed il numero M (minore o uguale ad N) di Threads a livello Kernel associati al medesimo Processo.
531.7.5.1. Multithreading in Java
Il linguaggio Java si presta bene alla modellazione di Processi multithreading. In Java ogni Thread è un'istanza della classe Thread del package java.lang.Thread. Ogni istanza della suddetta classe deve implementare al suo interno un metodo run() che sarà eseguito dal Thread, il quale dovrà essere riscritto mediante un override per essere adattato all'applicazione che si vuole realizzare. In realtà
si occupa della stampa a video della stringa "Pong". Entrambi i thread vengono avviati utilizzando il metodo start(). Un'altra possibilità per creare un thread in Java è implementare l'interfaccia Runnable e passare un'istanza di questa classe al costruttore di Thread. Ecco un esempio: ```java public class TableTennis{ public static void main(String[] args){ Ping ping = new Ping(); Thread thread1 = new Thread(ping); thread1.start(); Pong pong = new Pong(); Thread thread2 = new Thread(pong); thread2.start(); } } class Ping implements Runnable{ public void run(){ while(true){ System.out.println("Ping"); } } } class Pong implements Runnable{ public void run(){ while(true){ System.out.println("Pong"); } } } ``` In questo esempio, le classi Ping e Pong implementano l'interfaccia Runnable e quindi devono implementare il metodo run(). I thread vengono creati passando un'istanza di Ping e Pong al costruttore di Thread, e poi vengono avviati utilizzando il metodo start().public class TableTennis{
public static void main(String[] args){
Ping player1=new Ping();
Pong player2=new Pong();
Thread thread1=new Thread(player1);
Thread thread2=new Thread(player2);
thread1.start();
thread2.start();
}
}
class Ping implements Runnable{
public void run(){
while(true){
System.out.println(“Ping”);
}
}
}
class Pong implements Runnable{
public void run(){
while(true){
System.out.println(“Pong”);
}
}
}
run(){ while(true){ System.out.println("Pong"); } }Il programma principale si preoccupa prima della costruzione degli oggetti player1 e player2, e successivamente della costruzione di due Threads (thread1 e thread2) usando il costruttore della classe Thread; i Threads sono poi lanciati con il metodo start(). Il risultato delle 2 strategie è lo stesso, la seconda possibilità è leggermente più complicata e si utilizza in genere per aggirare la limitazione che Java non consente l'ereditarietà multipla. In Java i Threads sono implementati a livello utente mediante librerie a supporto a tempo di esecuzione; la schedulazione dei Threads nello stesso Processo è personalizzata e può essere modificata dall'utente mediante il metodo setPriority(). Altri metodi erediti dalla classe Thread sono i seguenti: - public void start() - Lancia il Thread; - public void run() - Esegue il codice; - public final void destroy() - Distrugge il Thread.
Thread;
public final void stop()
- Arresta il Thread;public final void suspend()
- Sospende il Thread;public final void resume()
- Riattiva il Thread;public final void sleep(long n)
- Sospende il Thread per n millisecondi;public final void setPriority(int newPriority)
- Modifica la priorità;public final int getPriority()
- Ritorna la priorità del Thread;public stati void yeld()
- Fa tornare il Thread in coda di Schedulazione;public final boolean isAlive()
- Torna true se il Thread è ancora in esecuzione;
L'uso di questi metodi può condizionare lo stato attuale di un Thread, quest'ultimo quando è stato appena creato con l'apposito costruttore della classe Thread viene collocato in uno stato chiamato New Thread. Il Thread può da questo stato passare allo stato Dead mediante una chiamata al metodo stop() oppure essere messo nello stato Runnable (che caratterizza tutti i Threads che possono essere
mandati in esecuzione) mediante una chiamata al metodo start(). Ancora una volta, da qui il Thread può finire nello stato Dead se arriva una chiamata al metodo stop() oppure se il metodo run() del Thread esegue l'ultima istruzione. Il metodo yeld() se applicato su di un Thread in stato Runnable rimanda il Thread in coda nella lista dei Threads in stato Runnable. I metodi suspend(), sleep() e wait() portano un Thread Runnable in stato Not55Runnable. Da qui, con una certa frequenza che varia da programma a programma, un Thread può ritornare a far parte dei Threads in stato Runnable mediante le chiamate resume() e notify(), oppure può finire in stato Dead mediante una chiamata al metodo stop(). Il tutto viene sintetizzato nella seguente immagine: Java mette a disposizione un modo semplice per regolamentare l'accesso alle regioni critiche attraverso il modificatore synchronized: quando questo modificatore viene messo davanti alla dichiarazione di un metodo, il run
time support assicura che quel metodo può essere eseguito da un solo Thread per volta (Mutua Esclusione). Se un metodo synchronized è definito in un oggetto e viene utilizzato, mentre questo è attivo non può essere richiamato nuovamente il metodo synchronized, cioè tale metodo non viene schedulato finché esso non ha terminato il suo compito nella regione critica. Utilizzare quindi il modificatore synchronized può rallentare (di molto se si usa anche static) l'esecuzione del programma, ma al contempo salvaguardarne l'integrità e la funzionalità. Infatti per come è fatto Java, se scatta un metodo synchronized viene bloccato l'intero oggetto, per cui anche se su tale oggetto operavano altri metodi non synchronized, anche questi verranno bloccati; se poi il metodo è anche statico oltre che synchronized, tutti gli oggetti di quella classe vengono bloccati allo scattare di un metodo synchronized su
che nessun altro Thread possa interromperlo. Questa soluzione è implementata a livello di hardware e richiede il supporto del processore. Soluzione di tipo Software: utilizza algoritmi e strutture dati per garantire la mutua esclusione tra i Threads. Un esempio di soluzione software è l'utilizzo dei semafori, che permettono di sincronizzare l'accesso alla regione critica. Ecco un esempio di formattazione del testo utilizzando tag html:Una soluzione al problema della regolamentazione degli accessi ad una Regione Critica mediante un Meccanismo di Mutua Esclusione deve rispettare 4 requisiti:
- Non devono mai essere due (o più) Threads attivi contemporaneamente nella medesima regione critica;
- La soluzione non deve essere basata sulla schedulazione o sulla velocità relativa di esecuzione del Thread, in quanto questi meccanismi non sono sotto il controllo del Programmatore;
- Un Thread che non è nella regione critica non deve bloccare nessun altro Thread che cerca di entrarvici;
- Un Thread non deve mai attendere all'infinito prima di entrare in una regione critica (per ovviare a questo problema si utilizza sempre una coda FIFO);
Ci sono 2 possibili categorie di soluzioni che rispecchiano questi requisiti e sono:
- Soluzione di tipo Hardware: consiste nella disabilitazione degli Interrupt per il Thread che entra nella regione critica, in modo che nessun altro Thread possa interromperlo. Questa soluzione è implementata a livello di hardware e richiede il supporto del processore.
- Soluzione di tipo Software: utilizza algoritmi e strutture dati per garantire la mutua esclusione tra i Threads. Un esempio di soluzione software è l'utilizzo dei semafori, che permettono di sincronizzare l'accesso alla regione critica.
da assicurarsi che non può essere interrotto o deschedulato mentre esso esegue delle operazioni su variabili globali, per poi riabilitarli nel momento in cui il Thread esce dalla regione critica. Purtroppo utilizzare questa soluzione è quasi impossibile, a patto di non modificare il Kernel del SO. Infatti per motivi di sicurezza è impossibile disabilitare gli Interrupt a livello utente, e quindi questa azione potrebbe essere utilizzata in modo malizioso da altri Threads per monopolizzare la CPU. Inoltre in Sistemi Multi-Processore non sarebbe possibile attuare la Mutua Esclusione in questo modo poiché un Thread potrebbe disabilitare le interruzioni sulla CPU da esso utilizzata, ma non su tutte le altre.
Soluzione di tipo Software: consiste nella creare di una porzione di codice che gestisce la Mutua Esclusione.
Ci sono diverse soluzioni di Gestione della Mutua Esclusione implementabili a livello Software, alcune di esse utilizzano un'attesa attiva, ovvero
testano in continuazione il valore di una variabile causando molto spreco di CPU. La prima soluzione software analizzata è l'Alternanza Stretta: è una soluzione con attesa attiva nella quale una variabile intera indica il turno del Thread che può entrare nella regione critica.
//Thread 0
//Thread 1
while(true){
while(true){
while(turno!=0)
regione_critica();
while(turno!=1)
regione_critica();
turno=1;
turno=0;
regione_non_critica();
regione_non_critica();
}
}
Il Thread 0 prima di entrare nella sezione critica controlla il valore della variabile turno e se la trova pari a 0 entra nella regione critica. Anche il Thread 1 legge la variabile turno ma quest'ultimo trovandola diversa da 1 entra in un ciclo di attesa attiva nel quale legge continuamente il valore della variabile turno sprecando inutili cicli di clock. Quando il Thread 0 esce dalla regione critica esso aggiorna la variabile turno ad 1 ed il Thread 1 (che sta leggendo in continuazione la variabile turno)```html
può adesso entrare nella regione critica. All'uscita della regione critica il Thread 1 rimette la variabile turno a 0. Questo provoca un'alternanza stretta con attesa attiva fra i Threads 0 ed 1: si tratta di una situazione inaccettabile dal momento che un Thread più lento può ritardare notevolmente l'altro bloccandone l'accesso alla regione critica. Altra soluzione software di Gestione della Mutua Esclusione è la Soluzione di Peterson:
#define False 0
#define True 1
#define N 2 //numero di Threads che vogliono accedere alla regione critica
int turn;
int interessato[N];
void enter(int tid) {
/* questa funzione gestirà gli accessi alla regione critica */
int altro = 1 - tid; /* l'opposto del Thread attivo è l'altro Thread */
interessato[tid] = True;
turn = tid;
while(turn == tid && interessato[altro] ==
```