Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Dipartimento di Informatica
Laboratorio di Reti - A
Lezione 1
JAVA Thread Programming:
Introduzione, thread pooling
20/09/2018
Laura Ricci
• Ricevimento
●
venerdì ore 14.00-17.00, nel mio studio, in qualsiasi momento per e-mail
Stream ed IO
●
Streams: tipi di streams, composizione di streams
●
meccanismi di serializzazione
●
serializzazione standard di JAVA: problemi
●
JSON
continua....
●
Programmazione di rete a basso livello
●
connection oriented Sockets
●
connectionless sockets: UDP, multicast
●
Non blocking IO e sockets
●
selectors
●
Oggetti Distribuiti
●
definizione di oggetti remoti
●
il meccanismo di Remote Method Invocation in JAVA
●
dynamic code loading
●
problemi di sicurezza
il meccanismo delle callbacks
• 8: Lambda Expressions
• sviluppato in parte da Doug Lea, disponibile ,come insieme di librerie JAVA non
standard prima della integrazione in JAVA 5.0.
●
come gestire tutte queste funzionalità simultaneamente? una decomposizione
del programma in threads implica:
●
modularizzazione della struttura dell’applicazione
●
aumento della responsiveness
●
altre applicazioni complesse che richiedono la gestione contemporanea di più
attività ad esempio: applicazioni interattive distribuite come giochi multiplayers
●
interazione con l'utente + messaggi da altri giocatori (dalla rete...)
●
ogni client deve aspettare la terminazione del servizio della richiesta
recedente
●
responsiveness limitata
●
struttura di un server multithreaded
●
un insieme di worker thread, uno per ogni client
●
aumento responsiveness
• ogni thread durante la sua esecuzione può creare ed attivare altri threads.
●
si attivino 10 threads
●
ogni numero n, 1 n 10, viene passato ad un thread diverso
●
il task assegnato ad ogni thread consiste nello stampare la tabellina
corrispondente al numero che gli è stato passato come parametro
Thread-0: 1 * 1 = 1
Thread-9: 10 * 1 = 10
Thread-5: 6 * 1 = 6
Thread-8: 9 * 1 = 9
Thread-7: 8 * 1 = 8
Thread-6: 7 * 1 = 7
Avviato Calcolo Tabelline
Thread-4: 5 * 1 = 5
Thread-2: 3 * 1 = 3
ogni metodo run() viene eseguito all'interno del flusso del thread attivato
per l'esecuzione del programma principale
allocare un'istanza T di R
• segnala allo schedulatore (tramite la JVM) che il thread può essere attivato
(invoca un metodo nativo). L'ambiente del thread viene inizializzato.
Costruttori:
• diversi costruttori che differiscono per i parametri utilizzati
●
nome del thread, gruppo a cui appartine il thread,...(.vedere le API)
Metodi
• possono essere utilizzati per interrompere, sospendere un thread, attendere
la terminazione di un thread + un insieme di metodi set e get per impostare e
reperire le caratteristiche di un thread
●
esempio: assegnare nomi e priorità ai thread
}
Se in un istante compreso tra l'inizio e la fine della sleep (inizio e fine inclusi), il
flag “interrupted” ha valore vero, allora l'eccezione viene lanciata
• resource consumption
molti threads idle quando il loro numero supera il numero di processori
disponibili. Alta occupazione di risorse (memoria,....)
sia il garbage collector che lo schedulatore sono 'sotto stress'
• un thread per ogni task risulta una soluzione improponibile, specialmente nel
caso di lightweight tasks molto frequenti.
• esiste un limite oltre il quale non risulta conveniente creare ulteriori threads
• obiettivi:
definire un limite massimo per il numero di threads che possono essere
attivati concorrentemente in modo da;
●
sfruttare al meglio i processori disponibili
●
evitare di avere un numero troppo alto di threads in competizione per
le risorse disponibili
diminuire il costo per l'attivazione/terminazione dei threads
• Thread Pool
●
struttura dati la cui dimensione massima può essere prefissata, che contiene
riferimenti ad un insieme di threads
●
i thread del pool possono essere riutilizzati per l'esecuzione di più tasks
●
la sottomissione di un task al pool viene disaccoppiata dall'esecuzione del
thread.
●
l'esecuzione del task può essere ritardata se non vi sono risorse disponibili
●
il meccanismo introdotto permette una migliore strutturazione del codice
poichè tutta la gestione dei threads può essere delegata al supporto
●
alcune interfacce definiscono servizi generici di esecuzione:
la classe Executors che opera come una Factory in grado di generare
oggetti di tipo ExecutorService con comportamenti predefiniti.
i tasks devono essere incapsulati in oggetti di tipo Runnable e passati a
questi esecutori, mediante invocazione del metodo execute()
• se tutti i thread del pool sono occupati nell'esecuzione di altri task e c'è un
nuovo task da eseguire, viene creato un nuovo thread.
●
Elasticità: “un pool che può espandersi all'infinito, ma si contrae quando la
domanda di esecuzione di task diminuisce”
import java.util.*;
public class Task implements Runnable {
private String name;
public Task(String name){
this.name=name;
}
import java.util.*;
public class Task implements Runnable {
private String name;
public Task(String name){ this.name=name;}
public void run() {
System.out.printf("%s: Task %s \n",
Thread.currentThread().getName(),name);
try{
Long duration=(long)(Math.random()*10);
System.out.printf("%s: Task %s: Doing a task during %d seconds\n",
Thread.currentThread().getName(),name,duration);
Thread.sleep(duration);
}
catch (InterruptedException e) {e.printStackTrace();}
System.out.printf("%s: Task Finished %s \n",
Thread.currentThread().getName(),name);}}}
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception{
Server server=new Server();
for (int i=0; i<10; i++){
Task task=new Task("Task "+i);
server.executeTask(task);
Thread.sleep(5000);
}
server.endServer();}}
La sottomissione di tasks al pool viene distanziata di 5 secondi. In questo modo
l'esecuzione precedente è terminata ed il programma riutilizza dempre lo stesso
thread.
alla sottomissione di un nuovo task, se tutti i thread del core sono stati creati
●
se un thread del core è inattivo, il task viene assegnato ad esso
●
altrimenti, se la coda passata come ultimo parametro del costruttore, non è
piena, il task viene inserito nella coda
●
i task vengono poi prelevati dalla coda ed inviati ai thread disponibili
●
altrimenti (coda piena e tutti i thread del core stanno eseguendo un task) si
crea un nuovo thread attivando così k thread,
corePoolSize ≤ k ≤ MaxPoolSize
●
altrimenti (coda piena e sono attivi MaxPoolSize threads), il task viene
respinto
• E' possibile scegliere diversi tipi di coda (tipi derivati da BlockingQueue). Il tipo
di coda scelto influisce sullo scheduling.
newFixedThreadPool
newCachedThreadPool
ShutdownNow( )
• Un pool viene creato nello stato running, quando viene invocata una
ShutDown() o una ShutDownNow() passa allo stato shutting down, quando
tutti i thread sono terminati passa nello stato terminated
• void shutdown()
• List<Runnable> shutdownNow()
restituisce la lista di threads eliminati dalla coda
• boolean isShutdown()
• boolean isTerminated()
• boolean awaitTermination(long timeout, TimeUnit unit)
attende che il pool passi in stato Terminated
Per capire se l'esecuizione del pool è terminata:
• attesa passiva: invoco la awaitTermination( )
• attesa attiva: invoco ripetutatmente la isTerminated ( )
• Una persona si mette quindi prima in coda nella prima sala, poi passa nella
seconda sala.