Il modificatore - Politecnico di Torino

Modificatori
I modificatori (modifiers) sono prefissi da applicare in diverse combinazioni a metodi, variabili ed alle classi stesse.
¯ L’ordine in cui specificare i modificatori è irrilevante, ma è consigliato l’ordine seguente:
<access> static abstract synchronized <unusual> final native
– <access> permette di definire il controllo dell’accesso
– <unusual> (che può assumere uno dei valori volatile e transient) e synchronized definiscono
caratteristiche legate al multithreading
– native specifica che un metodo è implementato in un linguaggio nativo (di solito il linguaggio C)
¯ Nessuno modificatore è obbligatorio ma è buona norma specificarli tutte le volte che si intende chiarire l’uso o le
restrizioni applicate a variabili, metodi e classi.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 1
JAVA
Controllo di visibilità
Quando un metodo o una variabile sono visibili da una classe diversa da quella in cui sono definiti, i metodi
della classe possono chiamare il metodo o modificare la variabile .
µ Il controllo della visibilità di metodi e variabili acquista sempre maggiore importanza a mano a mano che il
programma cresce in complessità.
Il modificatore <access> permette di controllare la visibilità. Può assumere uno dei seguenti valori:
¯ public
¯ package
¯ protected
¯ private
Ciascuno dei quattro livelli di protezione p
è più restrittivo del precedente.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 2
JAVA
Controllo di visibilità
¯ Il modificatore public fornisce il livello massimo di visibilità di variabili, metodi e classi: chiunque può
utilizzarle o modificarle.
¯ Il modificatore package (in realtà oggi sostituito dall’assenza di modificatori e quindi non più esistente) rende
visibili variabili e metodi all’interno del gruppo di classi appartenenti allo stesso package. È il modificatore di
default.
– Se una classe appartenente ad un package fa riferimento ad un metodo o variabile con modificatore di default
in un package diverso, genera un errore di compilazione.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 3
JAVA
Il modificatore protected
Spesso le sottoclassi sono più legate alla superclasse di qualsiasi altra classe, perché
¯ le sottoclassi hanno necessità di accedere alle strutture dati interne delle loro superclassi
¯ le sottoclassi spesso devono modificare o migliorare la rappresentazioni dei dati delle superclassi
¯ le sottoclassi sono spesso scritte da persone che accedono al codice sorgente delle superclassi
Il modificatore protected limita la visibilità di variabili e metodi alle sottoclassi della classe corrente.
µ Permette di supportare l’astrazione dei dati in modo semplice
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 4
JAVA
Esempio: modificatore protected
Il seguente esempio evidenzia l’importanza del modificatore protected per garantire l’astrazione.
public class List {
protected BinaryTree theBinaryTree;
public class SortedList extends List {
public void insertInOrder(Object o) {
theBinaryTree.insertObject(o);
}
}
public Object[] theList() {
return theBinaryTree.asArray();
}
public void add(Object o) {
theBinaryTree.addObject(o);
}
}
¯ L’unica possibilità di accesso “esterno” è mediante il metodo theList(), che ritorna un array di oggetti.
µ Senza la conoscenza della variabile BinaryTree il metodo insert dovrebbe agire sull’array.
µ Poiché BinaryTree è dichiarata protected, il metodo insert può invece inserire nella lista sfruttando la
nozione di albero binario.
¯ Dall’esterno il fatto che la lista sia ordinata mediante un albero binario è del tutto invisibile.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 5
JAVA
Il modificatore private
Il modificatore private rende variabili e metodi visibili solo all’interno della classe in cui sono definiti.
¯ Tutti i dati che rappresentano stati interni alla classe o dati privati devono essere dichiarati private.
¯ Il modificatore private permette di incapsulare i dati nel modo migliore.
¯ Non è possibile specificare separatamente livelli di protezione in scrittura e lettura
µ la dichiarazione private evita che altre classi possano modificare variabili di istanza
µ una variabile di istanza (a meno che non sia una costante) dovrebbe sempre essere dichiarata private
Un’eccezione è costituita dalla variabile out che viene utilizzata nel metodo System.out.print(), che
per ovvi motivi deve essere pubblica.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 6
JAVA
Visibilità delle variabili di istanza
Per permettere la visibilità esterna delle variabili di istanza
¯ si dichiara private la variabile di istanza
¯ si definisce un metodo di accesso in lettura public
¯ si definisce un metodo di accesso in scrittura protected
In questo modo
µ È possibile separa la variabile stessa dalla sua visibilità in lettura e scrittura.
µ È possibile modificare l’implementazione della variabile senza dover riscrivere il metodi che la utilizzano.
public class ACorrectClass {
private String aUsefulString;
public String get_aUsefulString () {
return aUsefulString;
}
protected String set_aUsefulString (String s) {
aUsefulString s;
}
}
Per modificare la variabile si usa la notazione
set aUsefulString ( get aUsefulString() + " testo aggiunto");
Tale notazione corrisponde a scrivere x(12+5*x()); invece di x = 12+5*x;
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 7
JAVA
Il modificatore final
Il modificatore final
¯ se applicato ad una classe, indica che non è possibile creare sottoclassi di tale classe
¯ se applicato ad una variabile, indica che tale variabile è una costante
¯ se applicato ad un metodo, indica il metodo non può essere ridefinito dalle sottoclassi
Il motivo principale per dichiarare una classe final è impedire che istanze di tale classe create da altre sottoclassi
siano presenti nel sistema, normalmente per motivi di sicurezza o di efficienza.
Le variabili final devono essere inizializzate al momento della loro dichiarazione. Si comportano come una versione “tipata” del costrutto #define del linguaggio C.
public class AnotherCLass {
public static final int
aConstantInt = 123;
public
final String aConstantString = "Hello world";
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 8
JAVA
Il modificatore final
I metodi final non possono essere ridefiniti.
µ il compilatore ha la garanzia che tali metodi non potranno essere modificati da nessuno
µ il compilatore può inserire direttamente il codice del metodo all’interno dei metodi che lo chiamano
µ associare il modificatore final ad un metodo permette di ottenere un’esecuzione più efficiente
¯ Questa tecnica può essere utilizzata per rendere più efficienti i metodi che garantiscono l’accesso a variabili di
istanza.
¯ Tutti i metodi che appartengono ad una classe final oppure che sono private sono automaticamente final.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 9
JAVA
Il modificatore abstract
Le classi ai livelli più alti di una gerarchia di generalizzazione sono più generali, mentre quelle ai livelli più bassi
sono più specifiche.
Spesso la creazione di una superclasse è utilizzata per specificare comportamenti comuni a molte classi.
µ Si definisce una classe astratta, specificando il modificatore abstract.
Le classi abstract
¯ non possono essere istanziate (a parte ciò sono del tutto uguali alle altre classe)
¯ sono le sole che possono contenere metodi abstract (di cui è specificata solo l’interfaccia), oltre ai metodi
normali
public abstract class LaMiaPrimaClasseAstratta {
int instancevariable;
public abstract int UnMetodoCheLeSottoClassiDevonoRealizzare();
...
public int UnMetodoNormale() {
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 10
JAVA
Il modificatore abstract
Le sottoclassi devono definire l’implementazione dei metodi abstract delle loro superclassi.
public class SottoClasseConcreta extends LaMiaPrimaClasseAstratta {
public int UnMetodoCheLeSottoClassiDevonoRealizzare() {
...
}
}
Object
Object
a = new LaMiaPrimaClasseAstratta();
a = new SottoClasseConcreta();
// Illegale!
// OK
L’uso di una classe astratta che contenga solamente metodi astratti non è scorretto; è preferibile però utilizzare le
interfacce per ottenere lo stesso scopo.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 11
JAVA
Package
I package sono utilizzati in Java sia per categorizzare sia per raggruppare classi tra loro.
¯ Per creare un package è sufficiente scrivere nella prima linea nel file:
package ilMioPrimoPackage;
¯ Se l’istruzione package non è presente in un file, il codice fa parte di un package senza nome di default.
¯ I package possono essere organizzati in modo gerarchico.
package ilMioPrimoPackage.packageInterno;
Ad esempio, le librerie Java sono organizzate in
– un package di primo livello: java
– package di secondo livello: java.io, java.net, java.util, java.applet, java.lang
e java.awt.
¯ Di norma i package sono caratterizzati da un nome con lettera iniziale minuscola; le classi preferenzialmente
iniziano con una lettera maiuscola.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 12
JAVA
Nomi dei package
Per garantire l’unicità dei nomi dei package, si utilizza una tecnica di definizione dei nomi basata sull’organizzazione
logica in domini di Internet e sull’indirizzo (Internet) della società che sviluppa il package.
COM.sun.java.io
EDU.harvard.cs.project.ai.learning.myPackage
L’organizzazione gerarchica dei package deve riflettersi in una analoga organizzazione dei direttori in modo tale che
il compilatore sappia sempre dove trovare i file necessari:
¯ le classi sono cercate a partire dal path specificato nella variabile di ambiente CLASSPATH
¯ ogni file può contenere una sola classe public
Ad esempio, per il sistema operativo Unix, la classe java.awt.image.ColorModel si trova in un file
ColorModel.class in .../classes/java/awt/image.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 13
JAVA
L’istruzione import
Ogni riferimento ad una classe in un programma java è composto da una parte (implicita o esplicita) che identifica il
package a cui la classe appartiene.
Per semplificare la notazione ed evitare di scrivere per esteso il nome di ogni classe è possibile importare packages
(eventualmente utilizzando la wildcard ’*’) con il comando import.
import package;
import package.class;
import package.*;
¯ Il comando import deve essere specificato all’inizio del file, subito dopo l’istruzione (opzionale) package e
prima di qualsiasi definizione di classe o interfaccia.
¯ Può essere specificato un numero arbitrario di comandi import.
¯ Il comando import non “copia” le classi; permette soltanto di specificare il nome delle classi senza la loro
estensione completa.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 14
JAVA
L’istruzione import
¯ Possono essere importate solo le classi pubbliche, non quelle definite ad esempio con il modificatore package,
il modificatore di default.
¯ Eventuali classi non visibili direttamente ma necessarie al funzionamento di classi visibili importate saranno
ovviamente caricate dal compilatore, ma resteranno non disponibili in accesso esplicito alle altre classi.
¯ L’istruzione import java.lang.* è implicita.
¯ L’istruzione import pack1.* importa tutte le classi contenute in pack1, ma non quelle contenute nei
package gerarchicamente al di sotto di pack1.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 15
JAVA
Interfacce
Le interfacce
¯ sono analoghe alle classi astratte perché forniscono lo schema di metodi (non il codice) che altre classi implementeranno
¯ sono diverse dalle classi astratte perché non possono contenere né implementazioni di metodi, né definizioni di
variabili di istanza
Le interfacce possono essere implementate da una classe, che fornisce la definizione del corpo di tutti i metodi
specificati nell’interfaccia.
Permettono di realizzare l’ereditarietà multipla, più complessa dell’ereditarietà semplice.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 16
JAVA
Interfacce
La dichiarazione di una interfaccia è molto simile a quella di una classe.
public interface LaMiaInterfaccia {
public static final int
theAnswer = 42;
public abstract
int
metodoPazzo();
long
contatoreDiTutto = 0;
// Diventa public, static, final (costante)
long
etaDellUniverso();
// Diventa public, abstract
protected
int
unaCostante:
// VIETATO
private
int
altraCostante;
// VIETATO
¯ Tutti i metodi sono public e abstract.
¯ Tutte le variabili sono public, static, final, cioè sono costanti.
Interfacce e classi hanno le stesse caratteristiche; le interfacce però non sono istanziabili mediante il comando new.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 17
JAVA
Interfacce ed ereditarietà multipla
¯ Le classi in Java possono avere una sola superclasse
µ i comportamenti globali definiti da una classe astratta possono essere ereditati solo dalle sue sottoclassi
¯ Le classi in Java possono implementare un numero arbitrario di interfacce
µ l’ereditarietà dei comportamenti globali definiti da un’interfaccia non è vincolata da una gerarchia di classe
µ Le interfacce permettono di realizzare l’ereditarietà multipla di metodi.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 18
JAVA
Interfacce: un esempio
Supponiamo esistano due classi, la classe Frutto e la classe OggettiSferici. La classe Arancia è sicuramente una sottoclasse di Frutti, ma ci piacerebbe che condividesse lo stesso comportamento della classe OggettiSferici.
interface TuttaLaFrutta extends TuttoIlCibo {
void si_decompone();
void si_schiaccia();
....
}
interface OggettiSferici {
void sigira();
....
}
class Frutto extends Cibo
implements TuttaLaFrutta {
private Color ilColore;
private int giorniprimachesidecomponga;
....
}
class Arancia extends Frutto
implements OggettiSferici {
...
// eredito da Frutto la
...
// realizzazione di TuttaLaFrutta
}
Nel momento in cui si renda disponibile una classe Sfera, è possibile modificare la definizione di Arancia senza
modificare la sua visibilità dall’esterno.
class Sfera implements OggettiSferici {
private float
raggio;
....
}
class Arancia extends Sfera implements TuttaLaFrutta {...}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 19
JAVA
Gerarchie di interfacce
In parallelo e indipendentemente dalla gerarchia delle classi, è possibile definire una gerarchia di interfacce, con la
differenza che non esiste una superclasse comune a tutte le interfacce come la classe Object.
public interface LaMiaSecondaInterfaccia extends Interfaccia1, Interfaccia2 {
...
}
public class LaMiaClasse implements LaMiaSecondaInterfaccia {
...
}
¯ L’interfaccia LaMiaSecondaInterfaccia eredita tutti i metodi e le costanti di Interfaccia1 e di
Interfaccia2.
¯ La classe LaMiaClasse deve definire il corpo di tutti i metodi definiti nell’interfaccia
LaMiaSecondaInterfaccia e in tutte le sue superinterfacce (Interfaccia1 e Interfaccia2).
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 20
JAVA
Eccezioni
L’eccezione è una istanza della classe Throwable che permette la gestione degli errori e di eventi imprevisti.
Le eccezioni permettono di esprimere in modo esplicito, mediante la clausola throws, quali eventi eccezionali
(normalmente errori) un metodo oppure una classe pu ò creare.
public class LaMiaPrimaClasse {
public void unMetodoConEccezioni()
....
}
throws
LaMiaPrimaEccezione {
}
Se voglio creare una eccezione:
public class LaMiaPrimaClasse {
public void unMetodoConEccezioni() throws LaMiaPrimaEccezione {
....
if (qualcosaDiTerribileSuccede() ) {
throw new LaMiaPrimaEccezione();
// questa parte di codice non viene mai eseguita
}
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 21
JAVA
Eccezioni
Esempio di passaggio di eccezioni tra metodi.
public void unAltroMetodoConEccezioni() throws LaMiaPrima Eccezione {
LaMiaPrimaClasse aLMPC = new LaMiaPrimaClasse();
aLMPC.unMetodoConEccezioni();
}
Poiché all’interno del metodo unAltroMetodoconEccezioni() non gestisco l’eccezione LaMiaPrimaEccezione, che potrebbe essere creata dal metodo unMetodoConEccezioni(), devo passare tale eccezione ai
metodi che mi hanno chiamato mediante la clausola Throws.
Se voglio gestire all’interno di un metodo una eccezione senza passarla ai metodi chiamanti uso le istruzioni try e
catch:
public void unMetodoresponsabile() {
LaMiaPrimaClasse aLMPC = new LaMiaPrimaClasse();
try {
aLMPC.unMetodoConEccezioni();
} catch (LaMiaPrimaEccezione m) {
...
// eseguire codice particolarmente intelligente
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 22
JAVA
Eccezioni
Se voglio gestire una eccezione ma passarla comunque ai metodi chiamanti:
public void unMetodoresponsabile() throws LaMiaPrimaEccezione {
LaMiaPrimaClasse aLMPC = new LaMiaPrimaClasse();
try {
aLMPC.unMetodoConEccezioni();
} catch (LaMiaPrimaEccezione m) {
...
// eseguire codice particolarmente intelligente
throw m;
}
}
Poiché le eccezioni sono una classe, esiste anche una gerarchia di eccezioni.
La classe Throwable ha due sottoclassi: Exception e Errors.
La classe RunTimeException è a sua volta una sottoclasse della classe Exception.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 23
JAVA
Eccezioni
Non è necessario dichiarare le istanze delle classi Error e RunTimeException con la clausola throws poiché
si riferiscono ad eventi non causati direttamente dal programma, ma che si possono verificare in qualunque momento.
Esempio: OutOfMemoryError.
Esempi di eccezioni dichiarabili in una clausola throws:
ClassNotFoundException, InstantiationException, NoSuchMethodException.
La gestione delle eccezioni spesso si basa su strutture del tipo:
try {
unMetodoConEccezioni();
} catch (NullPointerException n) {
...
} catch (RunTimeException r) {
...
...
} catch (IOException i) {
...
} catch (LaMiaPrimaEccezione m) {
...
} catch (Exception e) {
...
}
throw m;
//
//
//
//
//
//
//
//
una sottoclasse di RuntimeException
gestisco la NullPointerException
una sottoclasse di Exception
gestisco le RunTImeExceptions
che non sono NullPointerException
una sottoclasse di Exception
gestisco le IOExcetion
la mia sottoclasse di Exception
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 24
JAVA
La clausola finally
La clausola finally permette di specificare azioni che devono sempre essere eseguite, anche se si verifica un tipo
qualsiasi di eccezione (per esempio rilasciare una risorsa esterna o chiudere un file).
MioFile f = new MioFile();
if (f.open("pippo")) {
try {
metodoEccezionale();
} finally {
f.close()
}
}
Il programma precedente si comporta in modo analogo a questo:
MioFile f = new MioFile();
if (f.open("pippo")) {
try {
metodoEccezionale();
} catch (Throwable t) {
f.close();
throw t;
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 25
JAVA
Gestione I/O
La gestione dell’I/O in Java è basata sul concetto di stream.
Una stream è un percorso di comunicazione tra l’origine di un’informazione e la sua destinazione.
¯ L’informazione può provenire da qualsiasi origine: la memoria del calcolatore, un file, Internet.
¯ La sorgente è un produttore arbitrario di dati (per esempio byte).
¯ La destinazione è un consumatore arbitrario di dati (per esempio byte).
Il package java.io contiene la maggior parte delle classi che permettono di gestire l’I/O in Java è java.io.
Quasi tutte le stream (oltre 60) derivano da quattro classi astratte principali:
¯ InputStream e OutputStream per la gestione dell’I/O di stream di byte
¯ Reader e Writer per la gestione dell’I/O di stream di caratteri (Unicode)
Tutti i metodi che gestiscono l’I/O generano eccezioni appartenenti alla classe IOException (sottoclasse di Exception), che include tutti gli errori di I/O che possono avvenire leggendo o scrivendo stream.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 26
JAVA
La gerarchia delle classi in java.io (1/2)
Object
InputStream
ByteArrayIS
BufferedIS
FileIS
FilterIS
DataIs
RandomAccessFile
File
PipedIS
LuneNumberIS
SequenceIS
StringBufferIS
StreamTokenizer
ByteArrayOS
PushbackIS
OutputStream
FileOS
FilterOS
PipedOS
BufferedOS
DataOS
PrintStream
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 27
JAVA
La gerarchia delle classi in java.io (2/2)
Object
Reader
InputStreamR
FileR
CharArrayR
FilterR
PushbackIS
Writer
PipedR
BufferedR
StringR
OutputStreamW CharArrayW
LuneNumberIS
FilterW
PipedW
BufferedW
StringW
PrintW
FileW
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 28
JAVA
Il package java.io
Le classi principali sono:
¯ InputStream è una classe astratta che definisce le modalità principali di lettura di dati da una stream.
Reader è la classe astratta equivalente per la lettura di caratteri.
¯ OutputStream è una classe astratta che definisce le modalità principali di scrittura di dati in una stream.
Writer è la classe astratta equivalente per la scrittura di caratteri.
¯ File definisce il concetto di file in modo indipendente dalla piattaforma. Dato il nome di un file o di un direttorio,
permette di trovare informazioni descrittive, quali il tipo, lo stato ed altre proprietà.
¯ RandomAccessFile è la classe che implementa le interfacce DataInput e DataOutput, dedicate all’accesso
casuale a file. Fornisce il metodo seek().
¯ StreamTokenizer legge una stream e produce una sequenza di token. È possibile definire parser lessicali ridefinendo i metodi di questa classe.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 29
JAVA
Metodi della classe astratta InputStream
¯ read(): legge bytes dalla stream. Il metodo read() si blocca in attesa che tutta l’informazione richiesta sia
disponibile. In questo caso è opportuno definire un thread apposito per gestire la lettura di dati dalla stream.
InputStream s = cercaUnaStream();
byte[] buffer = new byte[1024];
byte b;
if (s.read(buffer) != buffer.length)
// legge l’intero buffer
System.out.println("Troppo pochi dati in ingresso");
s.read(buffer,100,300);
b = (byte)s.read();
// legge 300 byte e scrive in buffer dal byte 100
// legge un byte (ma read() ritorna un int)
– il metodo read() restituisce il numero di byte effettivamente letti
– quando si raggiunge la fine della stream, restituisce -1
µ nella classe astratta Reader il metodo read() permette di leggere char da una stream
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 30
JAVA
Metodi della classe astratta InputStream
¯ skip(): permette di ignorare un blocco di dati.
if (s.skip(1024) != 1024)
// salta 1024 bytes
System.out.println("Ci sono meno di 1024 byte da saltare");
– il metodo skip() accetta come parametro un long per indicare il numero di byte da ignorare (ma nell’implementazione del metodo è eseguito un cast a int!)
µ nella classe astratta Reader il metodo skip() permette di saltare char da una stream (e la sua implementazione
correttamente salta un numero long di caratteri)
¯ available(): restituisce il numero di bytes presenti nella stream (cioè il numero di byte che può essere letto
senza bloccarsi). È possibile che alcune sottoclassi concrete non siano in grado di rispondere; in questo caso è
restituito il valore 0.
¯ ready(): restituisce true se ci sono dati da leggere. Se la sottoclasse non è in grado di rispondere, restituisce
false.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 31
JAVA
Metodi della classe astratta InputStream
¯ close(): permette di chiudere una stream. Si utilizza il costrutto finally per garantire che l’istruzione
close sia sempre eseguita.
if (s != null) {
// verifica che la stream esista
try {
...
// uso della stream s
} finally {
s.close()
}
}
¯ mark() e reset(): permettono di memorizzare una posizione nella stream e successivamente di ritornare a
quella posizione.
if (s.markSupported()} {
...
s.mark(1024);
...
s.reset();
}
// e‘ supportato il marking?
//lettura della stream
// lettura di meno di 1024 byte (se supero, reset() non funziona)
// riposiziona al mark
µ a differenza di InputReader, nella classe astratta Reader il metodo mark() genera una IOException
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 32
JAVA
Tipi di input stream
¯ ByteArrayInputStream crea una stream a partire da un array di byte.
ByteArrayInputStream s = new ByteArrayInputStream(buffer); //crea stream di 1024 byte
ByteArrayInputStream s = new ByteArrayInputStream(buffer,100,300);
// crea stream di 300 byte a partire dal byte 100 di buffer
µ la classe Reader corrispondente è CharArrayReader
¯ FileInputStream collega una stream ad un file. Implementa sia available() e ready(), sia skip().
FileInputStream s = new FileInputStream("/miodir/miofile");
FileInputStream s = new FileInputStream(FileDescriptor.in); /* apre stdin */
FileInputStream s = new FileInputStream(new File("/miodir/miofile"));
FileDescriptor myFD = s.getFD();
// restituisce il file descriptor del file
µ la classe Reader corrispondente è FileReader (che non dispone del metodo getFD())
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 33
JAVA
Tipi di input stream
¯ FilterInputStream è una classe “astratta” che permette di definire classi “filtro”, che eseguono operazioni di
elaborazione dei dati letti. È possibile annidare un numero arbitrario di filtri.
InputStream s = cercaUnaStream();
FilterInputStream s1 = new FilterInputStream(s);
FilterInputStream s2 = new FilterInputStream(s1);
FilterInputStream s3 = new FilterInputStream(s2);
s3.read()
// la richiesta passa attraverso s2 e s1 prima di leggere da s
oppure
s3 = new FilterInputStream (new FilterInputStream(new FilterInputStream(s)));
µ la classe Reader corrispondente è FilterReader, che è una classe astratta, di cui non si possono creare
istanze
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 34
JAVA
Tipi di filtro
¯ BufferedInputStream utilizza un array di byte come cache per letture successive. In questo modo, si disaccoppia
la dimensione dei blocchi di dati letti dalla stream (grandi e di dimensione regolare) da quelli (più piccoli e di
dimesione irregolare) elaborati dal programma.
Può essere utilizzata come filtro per altre stream.
InputStream s = new BufferedInputStream (new FileInputStream("pippo"));
µ la classe Reader corrispondente è BufferedReader, che dispone inoltre del metodo readline() per
leggere una linea di testo (terminata con \r, \n o \r\n)
¯ LineNumberInputStream permette di tener traccia dei numeri delle linee durante la lettura della stream.
LineNumberInputStream aLNIS;
aLNIS = new LineNumberInputStream (new FileInputStream("pippo"));
DataInputStream s = new DataInputStream (aLNIS);
String line;
while ((line = s.readLine()) != null) {
...
/elabora la riga
System.out.println("Elaborata la riga numero " + aLNIS.getLineNumber());
}
µ la classe Reader corrispondente è LineNumberReader
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 35
JAVA
Tipi di filtro
¯ PushbackInputStream è utilizzata nei parser per rimettere un solo carattere nella stream di input dopo averlo
letto per decidere la prossima operazione da eseguire. Aggiunge il nuovo metodo unread(), che rimette nella
stream il byte ricevuto come parametro.
nextByte = (byte)s.read();
if (condizione particolare)
s.unread(nextByte);
nextByte = (byte)s.read(); // se condizione particolare e‘ true, rilegge lo stesso byte
In Java 1.1 sono stati introdotti nuovi metodi per rimettere nella stream di input un intero buffer o una sua parte
(in modo duale ai corrispondenti metodi read()).
µ la classe Reader corrispondente è PushbackReader
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 36
JAVA
Tipi di filtro
¯ DataInputStream implementa l’interfaccia DataInput, che specifica un insieme di metodi per leggere dati
dei tipi primitivi di Java. Tutti i metodi generano IOException.
void readFully(byte[] buffer);
// equivalente a read()
void readFully(byte[] buffer, int offset, int length);
int SkipBytes(int n);
// equivalente a skip()
boolean
byte
int
String
...
readBoolean();
readByte();
readInt();
readLine();
Quando è raggiunta la fine della stream, questi metodi (a parte readLine() e readUTF()) generano una
EOFException.
DataInputStream s = new DataInputStream(cercaUnaStream());
try {
while(true) {
byte b = (byte) s.readByte();
...
// elaborazione del byte
}
} catch (EOFException e) {
...
// raggiunta la fine della stream
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 37
JAVA
Tipi di input stream
¯ PipedInputStream, insieme a PipedOutputStream, permette di costruire un canale di comunicazione bidirezionale tra thread.
µ le classi Reader/Writer corrispondenti sono PipedReader e PipedWriter
¯ SequenceInputStream permette di creare una stream come sequenza di due (o più) stream.
InputStream s1 = new FileInputStream("primaParte");
InputStream s2 = new FileInputStream("secondaParte");
InputStream s = new SequenceInputStream(s1,s2);
... s.read() ... // legge dalla concatenazione delle stream
¯ StringBufferInputStream è come ByteArrayInputStream, ma è basata su un array di caratteri (String)
anziché di byte.
µ la classe Reader corrispondente è StringReader
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 38
JAVA
Tipi di input stream
¯ ObjectInputStream permette di leggere da una stream un oggetto. Implementa i metodi dell’interfaccia
ObjectInput (che a sua volta è un’estensione di DataInput).
Fornisce il nuovo metodo readObject() che ritorna un oggetto della classe Object e può generare le eccezioni ClassNotFoundException e IOException.
FileInputStream s = new FileInputStream("mioFile");
ObjectInputStream ois = new ObjectInputStream(s);
int
i = ois.readInt();
// metodo di DataInput
String frase = (String)ois.readObject();
Date
data = (Date) ois.readObject();
s.close();
La serializzazione permette di trasformare un oggetto in una stream e, in seguito, ritrasformarlo in un oggetto.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 39
JAVA
Metodi della classe astratta OutputStream
¯ write(): scrive byte sulla stream. Si blocca fino a quando tutti i dati non sono stati scritti sulla stream.
OutputStream s = cercaUnaStream();
byte[] buffer = new byte[1024];
byte b;
s.write(buffer);
s.write(buffer,100,300);
s.write(b);
// scrive 300 byte a partire dal byte 100
// scrive un byte
µ nella classe astratta Writer il metodo write() permette di scrivere char in una stream. Sono inoltre
disponibili due metodi aggiuntivi per scrivere una stringa (write(String)) e una parte di stringa.
¯ flush() permette di forzare lo svuotamento di un’eventuale cache in scrittura. È implementato in modo appropriato da ciascuna sottoclasse di OutputStream.
µ il metodo è presente anche nella classe astratta Writer
¯ close() chiude la stream.
µ il metodo è presente anche nella classe astratta Writer
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 40
JAVA
Tipi di output stream
¯ BytearrayOutputStream dirige una stream di output in un array di byte. La dimensione dell’array interno
cresce in modo da contenere una stream di lunghezza arbitraria.
OutputStream s = new ByteArrayOutputStream();
OutputStream s1 = new ByteArrayOutputStream(1024*1024);
// stream di 1Mbyte
s.writeTo(s1)
// scrive s su s1
int dimArray = s.size():
s.reset();
// s.size() ora ritornerebbe 0
µ la classe Writer corrispondente è CharArrayWriter
¯ FileOutputStream collega una stream di output ad un file. Dispone di metodi analoghi a FileInputStream.
FileOutputStream s =
FileDescriptor aFD =
FileOutputStream out
FileOutputStream err
new FileOutputStream("/miodir/miofile");
s.getFD(); /* restituisce il file descriptor del file */
= new FileOutputStream(FileDescriptor.out); /* scrive su stdout */
= new FileOutputStream(FileDescriptor.err); /* scrive su stderr */
µ la classe Writer corrispondente è FileWriter
¯ FilterOutputStream è analoga a FilterInputStream; permette di definire classi “filtro”, che eseguono
operazioni di elaborazione dei dati scritti.
µ la classe Writer corrispondente è FilterWriter
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 41
JAVA
Filtri di output
¯ BufferedOutputStream è analoga a BufferedInputStream; utilizza un array come cache in scrittura.
Implementa il metodo flush().
OutputStream s = new BufferedOutputStream(new FileInputStream("miofile"));
µ la classe Writer corrispondente è BufferedWriter, che dispone inoltre del metodo newline() che
invia in output uno o più caratteri per la nuova linea adatti al sistema operativo corrente
¯ DataOutputStream implementa l’interfaccia DataOutput, che specifica un insieme di metodi per scrivere i
dati come tipi primitivi di Java. Tutti i metodi generano IOException.
L’elaborazione di un file può essere eseguita in questo modo:
DataInput aDI = new DataInputStream(new FileInputStream("source"));
DataOutput aDO = new DataOutputStream(new FileOutputStream("dest"));
String line;
while ((line = aDI.readLine()) != null) {
... // elaborazione linea
aDO.writeChars(line);
}
aDI.close();
aDO.close();
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 42
JAVA
Filtri di output
¯ PrintStream è collegata a dispositivi di output. Contiene i metodi per eseguire la stampa di tutti i tipi primitivi
di Java (print() e println()). Implementa flush().
System.out.println(...);
// out contiene un’istanza di PrintStream
Esempio: programma che realizza il comand cat di Unix (legge lo standard input una linea per volta e lo scrive
sullo standard output).
public class Cat {
public static void main(String argv[]) {
DataInput d = new DataInputStream(System.in);
String
line;
try ( while((line = d.readLine()) != null)
System.out.println(line);
} catch (IOException ignored) { }
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 43
JAVA
Tipi di output stream
¯ ObjectOutputStream permette di scrivere un oggetto in una stream. Implementa i metodi dell’interfaccia ObjectOutput (che a sua volta è un’estensione di DataOutput).
Fornisce il nuovo metodo writeObject(Object o) che scrive l’oggetto o in una stream e può generare
l’eccezione IOException.
FileOutputStream s = new FileOutputStream("mioFile");
ObjectOutputStream oos = new ObjectOutputStream(s);
oos.writeInt(12345);
// metodo di DataOutput
oos.writeObject("frase");
oos.writeObject(new Date());
oos.flush();
s.close();
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 44
JAVA
Esempio: visualizzazione di un file (1/2)
import java.awt.*;
import java.io.*;
import java.awt.event.*;
public class FileViewer extends Frame {
Button close;
// Query the size of the specified file, create an array of bytes big
// enough, and read it in. Then create a TextArea to display the text
// and a "Close" button to pop the window down.
public FileViewer(String filename) throws IOException {
super("FileViewer: " + filename);
File f = new File(filename);
int size = (int) f.length();
int bytes_read = 0;
FileInputStream in = new FileInputStream(f);
byte[] data = new byte[size];
while(bytes_read < size)
bytes_read += in.read(data, bytes_read, size-bytes_read);
TextArea textarea = new TextArea(new String(data), 24, 80);
textarea.setFont(new Font("Helvetica", Font.PLAIN, 12));
textarea.setEditable(false);
this.add("Center", textarea);
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 45
JAVA
Esempio: visualizzazione di un file (2/2)
close = new Button("Close");
close.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent event)
{ System.exit(0);}
});
this.add("South", close);
this.pack();
this.show();
}
// The FileViewer can be used by other classes, or it can be
// used standalone with this main() method.
static public void main(String[] args) throws IOException {
if (args.length != 1) {
System.out.println("Usage: java FileViewer <filename>");
System.exit(0);
}
try { Frame f = new FileViewer(args[0]); }
catch (IOException e) { System.out.println(e); }
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 46
JAVA
L’applicazione FileViewer
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 47
JAVA
Esempio: il filtro grep (1/2)
import java.io.*;
// This class is a FilterReader that filters out all lines that
// do not contain the specified substring.
public class GrepReader extends FilterReader {
String substring;
BufferedReader in;
public GrepReader(BufferedReader in, String substring) {
super(in);
this.in = in;
this.substring = substring;
}
// This is the filter: read lines from the DataInputStream,
// but only return the lines that contain the substring.
// When the DataInputStream returns null, we return null.
public final String readLine() throws IOException {
String line;
do {
line = in.readLine();
}
while ((line != null) && line.indexOf(substring) == -1);
return line;
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 48
JAVA
Esempio: il filtro grep (2/2)
import java.io.*;
// This class prints the lines of a file that contain a specified substring.
public class Grep {
public static void main(String args[]) {
if ((args.length == 0) || (args.length > 2)) {
System.out.println("Usage: java Grep <substring> [<filename>]");
System.exit(0);
}
try {
BufferedReader d;
if (args.length == 2)
d = new BufferedReader(new InputStreamReader(new FileInputStream(args[1])));
else
d = new BufferedReader(new InputStreamReader(System.in));
GrepReader g = new GrepReader(d, args[0]);
String line;
for(;;) {
line = g.readLine();
if (line == null) break;
System.out.println(line);
}
g.close();
}
catch (IOException e)
{ System.err.println(e); }
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 49
JAVA
Multithreading
¯ Un thread è un “sottoprocesso” all’interno di un programma
µ multithreading quando più thread eseguono all’interno dello stesso programma
¯ Più thread possono eseguire in parallelo la stessa porzione di codice
µ è necessario poter definire operazioni atomiche, cioè insiemi di istruzioni che gli altri thread vedono come
se “eseguissero in una volta sola”
¯ Se più thread eseguissero contemporaneamente il codice
public class ThreadCounter {
int crucialValue;
public void countMe() {
crucialValue +=1;
}
public int howMany() {
return crucialValue;
}
}
poiché l’operazione += richiede più di un passo, il valore di crucialValue potrebbe risultare sbagliato.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 50
JAVA
Multithreading
¯ Il modificatore synchronized nella definizione di un metodo permette di indicare che il codice eseguito dal
metodo deve essere atomico (thread-safe)
– il metodo può essere eseguito da un thread per volta
– è bloccato l’oggetto su cui il metodo è invocato
µ non è opportuno definire synchronized metodi complessi che eseguono operazioni lunghe
¯ Il codice corretto sarebbe in questo caso:
public class ThreadCounter {
int crucialValue;
public synchronized void countMe() {
crucialValue +=1;
}
public
int
howMany() {
return crucialValue;
}
}
¯ Il modificatore volatile permette di specificare che una variabile può essere aggiornata in modo asincrono da
più processi, quindi deve essere mantenuta in memoria e non all’interno di registri.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 51
JAVA
Multithreading
¯ Esempio di lettura di più variabili correlate:
public class Point {
private float x, y;
public float x() {
return x;
}
public float y() {
return y;
}
public class UnsafePointPrinter {
public void print(Point p) {
System.out.println("The point’s x is " p.x() +
" and y is " p.y() );
}
}
}
¯ I metodi x() e y() non richiedono sincronizzazioni poiché restituiscono il valore di una variabile.
¯ Il metodo UnsafePointPrinter invece deve garantire che le due istanze x e y non siano modificate da altri
thread durante l’esecuzione dell’istruzione println.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 52
JAVA
Multithreading
¯ L’istruzione synchronized permette di specificare che l’esecuzione di un blocco di codice deve avvenire in
modo atomico, bloccando l’accesso ad altri thread presenti nel sistema.
µ È opportuno, per motivi di efficienza, limitare al massimo il codice che deve essere bloccato da un thread.
public class TryAgainPointPrinter {
public void print(Point p) {
float safeX, safeY;
synchronized(this) {
safeX = p.x(); // queste due linee sono eseguite
safeY = p.y(); // in modo atomico
}
System.out.println("The point’s x is " safeX + " and y is " safeY );
}
}
¯ Nell’esempio, l’istruzione synchronized blocca l’istanza della classe TryAgainPointPrinter
µ l’esempio non è corretto poiché non è bloccato l’oggetto p.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 53
JAVA
Multithreading
¯ Il metodo print() corretto:
public class SafePointPrinter {
public void print(Point p) {
float safeX, safeY;
synchronized(p) { // nessuno puo‘ modificare p
safeX = p.x(); // mentre queste due linee sono eseguite
safeY = p.y(); // in modo atomico
}
System.out.println("The point’s x is " safeX + " and y is " safeY );
}
}
¯ Per rendere sicura la classe Point, è necessario definire un unico metodo sicuro per l’aggiornamento delle
variabili:
public synchronized
void setXandY (float newX, float newY) {
x = newX;
y = newY;
}
public class Point {
private float x, y;
public float x() {
return x;
}
public float y() {
return y;
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 54
JAVA
Multithreading
L’uso del modificatore synchronized permette anche di garantire che possa essere eseguito solo un metodo alla
volta (bloccando l’oggetto su cui è invocato).
public class ReallySafePoint {
private float x, y;
public synchronized void
setXandY (float newX, float newY) {
x = newX;
y = newY;
}
public synchronized Point getUniquePoint() {
return new Point(x,y);
// non devo piu‘ sincronizzare quando
}
// faccio get, perche restituisce un oggetto
public synchronized void
scale (float scaleX, float scaleY) {
x *= scaleX;
y *= scaleY;
}
public synchronized void
add (ReallySafePoint aRSP) {
Point p = aRSP.getUniquePoint(); // uso il metodo protetto per leggere
x += p.x();
// nessuno conosce l’esistenza di p
y += p.y();
// quindi non e‘ necessario proteggerlo
}
// alla fine p e‘ scartato da garbage collection
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 55
JAVA
Protezione delle variabili di classe
¯ Il modificatore synchronized blocca l’oggetto su cui il metodo è invocato
public class StaticCounter {
private static int crucialValue;
public synchronized void countMe() {
crucialValue +=1;
}
}
// blocco l’istanza
µ Il modificatore synchronized blocca solo l’istanza su cui è invocato il metodo countMe(), ma non la
variabile di classe (la variabile di classe potrebbe essere modificata utilizzando istanze diverse della stessa
classe).
¯ Per bloccare una variabile di classe, è necessario bloccare la classe a cui appartiene
public class StaticCounter {
private static int crucialValue;
public void countMe() {
synchronized(getClass()); {
crucialValue +=1;
}
}
// blocco la classe
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 56
JAVA
Creazione di thread
¯ Per definire un nuovo tipo di thread si crea una sottoclasse della classe Threads:
public class IlMioPrimoThread extends Thread {
public void run() {
...
// operazioni eseguite dal thread
}
}
¯ Per creare un thread:
IlMioPrimoThread aMPT = new IlMioPrimoThread();
aMPT.start();
// chiamo il metodo run() attivando il thread
aMPT.stop();
aMPT.suspend();
aMPT.resume();
// blocco il thread
// sospendo il thread
// faccio ripartire il thread
¯ Un thread è automaticamente sospeso e successivamente riattivato quando raggiunge un punto di sincronizzazione (delimitato da synchronized).
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 57
JAVA
Creazione di thread
Se la classe appartiene già ad una gerarchia, è possibile utilizzare l’interfaccia Runnable(), che contiene solo il
metodo run().
public class IlMioSecondoThread extends ClasseImportante
implements Runnable {
public void run() {
...
}
}
1. Si usa un’istanza di IlMioSecondoThread
IlMioSecondoThread
aMST = new IlMioSecondoThread();
2. Si crea un thread, specificando aMST come l’obiettivo del thread
Thread
aThread = new Thread(aMST);
3. l’invocazione di start() su aThread causa l’invocazione del metodo run() di aMST
aThread.start();
Versione abbreviata:
new Thread (new IlMioSecondoThread()).start();
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 58
JAVA
Schedulazione dei thread
¯ Lo scheduler è la parte del sistema che decide l’ordinamento real-time dei thread.
¯ Le modalità fondamentali di schedulazione sono:
– non-preemptive: lo scheduler fa eseguire il thread corrente “per sempre” e attende che il thread comunichi
esplicitamente quando è possibile far partire un altro thread
µ utilizzato in applicazioni real-time particolarmente critiche, in cui essere interrotti troppo a lungo oppure
nel momento sbagliato può causare danni rilevanti
– preemptive time-slicing: lo scheduler esegue il thread corrente fino a quando ha consumato il tempo a sua
disposizione, poi lo interrompe, lo sospende e fa ripartire un altro thread per l’intervallo di tempo successivo
µ più semplice scrivere programmi, perchè l’allocazione delle risorse è decisa completamente dallo scheduler (i programmi non devono eseguire yield() per cedere il controllo)
¯ È possibile definire un ordinamento totale tra i thread assegnando loro una priorità. I thread a priorità maggiore:
– eseguono più frequentemente, oppure ricevono un intervallo di esecuzione più lungo
– interrompono l’esecuzione di thread a priorità inferiore anche prima che sia terminato l’intervallo di tempo
loro assegnato
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 59
JAVA
Controllare il tipo di scheduler
¯ Lo scheduler di Java permette di assegnare priorità ai thread, ma non è definita la modalità con cui avviene la
schedulazione di thread con la stessa priorità.
µ dipende dalla piattaforma
¯ A parità di priorità, per controllare se lo scheduler Java si comporta come uno scheduler preemptive o non
preemtive si può utilizzare il seguente codice:
public class RunnablePotato implements Runnable {
public void run() {
while (true)
System.out.println(Thread.currentThread().getName());
}
}
public class PotatoThreadTester {
public static void main(String argv[]) {
RunnablePotato aRP = new RunnablePotato();
new Thread(aRP, "una patata").start();
new Thread(aRP, "due patate").start();
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 60
JAVA
Controllare il tipo di scheduler
¯ Risultato
Schedulazione non-preemptive
Schedulazione preemptive
una patata
una patata
una patata
una patata
...
una patata
una patata
...
una patata
due patate
due patate
...
due patate
¯ Per garantire che il comportamento sia preemptive con divisione di tempo:
public class RunnablePotato implements Runnable {
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.yield();
// cedo il controllo ad un altro thread
}
// abbreviazione di Thread.currentThread().yield()
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 61
JAVA
Gestione delle priorità
¯ I valori che rappresentano la priorità minima, normale e massima assegnabile a un thread sono variabili di classe
della classe Thread: Thread.MIN PRIORITY, Thread.NORM PRIORITY, Thread.MAX PRIORITY
(le priorità sono correntemente definite in un intervallo da 1 a 10 con valore normale 5).
¯ Per controllare la gestione delle priorità eseguita dallo scheduler:
public class PriorityTest {
public static void main(String argv[]) {
Runnable Potato aRP = new RunnablePotato();
Thread
t1 = new Thread(aRP, " one potato");
Thread
t2 = new Thread(aRP, " two potato");
t2.setPriority(t1.getPriority() + 1);
t1.start();
t2.start();
// alla priorita‘ Thread.NORM_PRIORITY+1
}
}
µ Se la prima riga è una patata, lo scheduler non interrompe considerando le priorità.
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 62
JAVA
Informazioni sui thread: la applet ThreadLister (1/3)
import java.io.*;
public class ThreadLister {
// Display info about a thread.
private static void print_thread_info(PrintStream out, Thread t,
String indent) {
if (t == null) return;
out.println(indent + "Thread: " + t.getName() +
" Priority: " + t.getPriority() +
(t.isDaemon()?" Daemon":"") +
(t.isAlive()?"":" Not Alive"));
}
// Display info about a thread group and its threads and groups
private static void list_group(PrintStream out, ThreadGroup g,
String indent) {
if (g == null) return;
int num_threads = g.activeCount();
int num_groups = g.activeGroupCount();
Thread[] threads = new Thread[num_threads];
ThreadGroup[] groups = new ThreadGroup[num_groups];
g.enumerate(threads, false);
g.enumerate(groups, false);
out.println(indent + "Thread Group: " + g.getName() +
" Max Priority: " + g.getMaxPriority() +
(g.isDaemon()?" Daemon":""));
for(int i = 0; i < num_threads; i++)
print_thread_info(out, threads[i], indent + "
");
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 63
JAVA
Informazioni sui thread: la applet ThreadLister (2/3)
for(int i = 0; i < num_groups; i++)
list_group(out, groups[i], indent + "
");
}
// Find the root thread group and list it recursively
public static void listAllThreads(PrintStream out) {
ThreadGroup current_thread_group;
ThreadGroup root_thread_group;
ThreadGroup parent;
// Get the current thread group
current_thread_group = Thread.currentThread().getThreadGroup();
// Now go find the root thread group
root_thread_group = current_thread_group;
// Se la RuntimePermission modifyThreadGroup non e’ garantita in .java.policy
// (tramite policytool), l’applet non riesce a visitare i threadgroup parenti
try { parent = root_thread_group.getParent(); }
catch (SecurityException e) { parent = null; }
while(parent != null) {
root_thread_group = parent;
parent = parent.getParent();
}
// And list it, recursively
list_group(out, root_thread_group, "");
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 64
JAVA
Informazioni sui thread: la applet ThreadLister (3/3)
import java.applet.*;
import java.awt.*;
import java.io.*;
public class AppletThreadLister extends Applet {
TextArea textarea;
// Create a text area to put our listing in
public void init() {
textarea = new TextArea(20, 60);
this.add(textarea);
Dimension prefsize = textarea.getPreferredSize();
this.resize(prefsize.width, prefsize.height);
}
// Do the listing. Note the cool use of ByteArrayOutputStream.
public void start() {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
ThreadLister.listAllThreads(ps);
textarea.setText(os.toString());
}
}
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 65
JAVA
La applet ThreadLister
Elena Baralis, Andrea Bianco, Maurizio Munaf ò
Politecnico di Torino
JAVA: APPROFONDIMENTI – 66
JAVA
JAVA