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