4. La notazione CamelCase

Andrea Pastore 19/03/2020 0

Quando scriviamo un programma e dobbiamo assegnare i nomi alle varie entità (variabili, classi, package ecc) non possiamo usare spazi, ad esempio non scriveremo

int numero oggetti;

ma scriveremo

int numeroOggetti;

la lettera iniziale della parola oggetti è scritta in maiuscolo, per una pratica nata negli anni 70 definita notazione CamelCase. Per facilitare la lettura del codice, i programmatori cominciarono a scrivere le inizali di ogni parola successiva alla prima in maiuscolo, ed è una pratica caldamente consigliata ancora oggi.

 

Potrebbero interessarti anche...

Andrea Pastore 19/03/2020

16. Il Java Collection Framework

Un framework è un insieme di classi, librerie o funzioni che aiutano i programmatori a sviluppare il proprio programma più velocemente. Il Java Collection Framework è una collezione di classi e interfacce sviluppata direttamente dai creatori del linguaggio Java. Le classi del Java Colection Framwork implementano strutture dati più evolute rispetto ad un array, che possono essere utilizzate in situazioni specfiche. Per Collection intendiamo una classe (o un’interfaccia che deve essere usata per creare una classe) che dispone di metodi per gestire un insieme di dati (ad esempio un insieme di stringhe, un insieme di interi ma anche dati più complessi, per esempio tipi di dati creati da noi). Esiste un’interfaccia chiamata Collection che viene estesa da tutte le interfacce del Java Collection Framework.

 

Le interfacce del Java Collection Framework sono:

  • List
  • Set
  • Map
  • Iterator
  • Queue

Per ogni interfaccia, tranne Iterator, abbiamo un’implementazione:

  • ArrayList
  • HashMap
  • HashSet
  • PriorityQueue (e molte altre, maggiori informazioni nel paragrafo dedicato)

Nota: a differenza dell’array che è un tipo di dato primitivo, quando vogliamo usare le strutture di dati presenti nel framework dobbiamo importarle.

 

Il tipo di dato generico

Tutte le classi del Java collection Framwork utilizzano una caratteristica di Java: i tipi generici (generics in inglese). Questo consiste nello specificare di volta in volta

quando si crea una determianta classe che tipo di dato si vuole utilizzare. Vediamo un esempio con l’ArrayList, la prima classe di questo framework che vedremo nella prossima pagina.

		ArrayList arr = new ArrayList<>();

La riga qui sopra definisce un ArrayList di stringhe. Il tipo di dato viene specificato subito dopo il nome della classe all’interno dei simboli <>. Questo simbolo va ripetuto anche nel costruttore, in questo caso vuoto. Per ora non soffermiamoci su questo punto, più avanti nel corso del libro capiremo come mai viene ripetuto e cosa può essere inserito al suo interno.

 

Gli ArrayList

La classe ArrayList è l’implementazione dell’interfaccia List, consente di memorizzare gli elementi un po’ come un array, ma con alcune funzionalità in più. Come per gli array dobbiamo specificare il tipo di dato, ma si fa in modo diverso:

		ArrayList arr = new ArrayList<>();

La riga qui sopra definisce un ArrayList di stringhe.

Nota: non abbiamo fatto menzione ai numeri, perché la dimensione di un ArrayList non è fissa. Appena lo creiamo sarà vuoto, poi crescerà man mano che inseriremo elementi.

Per inserire un elemento nell’ArrayList si usa il metodo Add. Il codice sottostante inserisce nell’ArrayList la parola “ciao”:

		arr.add("ciao");

Per recuperare un elemento dall’arrayList usiamo il  metodo get. Il codice qui sotto memorizza all’interno di una variabile (in questo caso Stringa) il valore alla posizione 0 dell’arrayList:

		//recuperiamo ora un elemento

String s = arr.get(0);

Per eliminare un elemento dall’arrayList usiamo il metodo remove. Il seguente codice  rimuoverà l’elemento alla posizione 0 dell’ArrayList:

		arr.remove(0);

Quando un elemento viene eliminato, la dimensione dell’arrayList si riduce, e gli elementi successivi indietreggiano di una posizione.

Per conoscere la dimensione di un ArrayList usiamo il metoto size, il seguente codice memorizza all’interno di una variabile la dimensione attuale dell’ArrayList

		int size = arr.size();

 Per sapere se un elemento è presente nel nostro ArrayList possiamo usare il metodo contains:

arr.contains(“nomeStringa”)

l’utilizzo più tipico è quello all’interno di un costrutto if:

		if(arr.contains(“nomeStringa”)) {

// eseguiamo del codice se nomeStringa è presente

}

Quando l’arrayList contiene tipi di dati complessi (esempio una classe che abbiamo definito) il metodo prenderà in input un oggetto di quel tipo e confronterà le locazioni di memoria.

 

Le mappe (HashMap)

Finora abbiamo visto strutture di dati che consentono un accesso basato sulla posizione: ad esempio per prendere un determinato elemento da un array dobbiamo usare il seguente codice:

		String s = arrayInt[0];

Se invece non sappiamo la posizione del nostro elemento, dobbiamo scorrere tutto l’array fino a quando non lo troviamo. Un modo alternativo di memorizzare gli elementi è quello di associare una chiave ad ognuno di essi. Ad esempio memorizzo una stringa e le assegno un nome per identificarla, ad esempio memorizzo un codice fiscale e come nome scrivo "mioCodiceFiscale" Questa stringa contenente il mio codice fiscale sarà immediatamente raggiungibile cercando la parola chiave "mioCodiceFiscale", invece di scorrere un array.

Creiamo adesso la nostra prima mappa, dobbiamo utilizzare (e quindi importare) la classe Hashmap. Nota: dobbiamo importare anche l’interfaccia Map.

import java.util.Map;

import java.util.HashMap;

Map<String,String> m = new HashMap<>();

Quando creiamo una mappa dobbiamo specificare due tipi di dati: quello della chiave

e quello dell'elemento che vogliamo memorizzare. Nel nostro caso, supponendo di voler memorizzare dei codici fiscali, avremmo come tipo di dato String per la chiave (ed codiceFiscaleGiovanni) e String per il valore (ES RSSGNN90S05A592N)

Per inserire un elemento in una mappa si usa il metodo put, che prenderà in input una coppia nome valore del tipo specificato al momento della creazione della mappa.

	m.put("CodiceFiscaleGiovanni", "RSSGNN90S05A592N");

Per recuperare un elemento dalla mappa si usa invece il metodo get. Continuando con il nostro esempio, per prendere il codice fiscale che abbiamo inserito prima scriveremo:

	String codiceFiscale = m.get("CodiceFiscaleGiovanni");

Nota: i nomi devono essere univoci, nell’esempio qui sopra abbiamo inserito nella mappa una chiave chiamata  CodiceFiscaleGiovanni, non possiamo inserire altre chiavi con lo stesso nome, altrimenti il valore precedente con la stesa chiave verrà sovrascritto.

Se infatti dopo aver scritto i codici sopra scriveremo:

 	m.put("CodiceFiscaleGiovanni", "RSSGNN74S09A592K");

Il valore precedentemente memorizzato verrà sovrascritto e quando proveremo a recuperare questo dato dalla mappa otterremo "RSSGNN74S09A592K"

Per rimuovere un elemento dalla mappa si usa il metodo remove che ha come parametro la parola chiave che abbiamo inserito al momento della creazione di quell’elemento. Ad esempio se voglio rimuovere il codice fiscale inserito sopra scriverò:

	m.remove("CodiceFiscaleGiovanni");

Facciamo ora un altro esempio: supponiamo di dover memorizzare i dati dei nostri utenti. Abbiamo una classe Utente che contiene i dati, e dobbiamo memorizzarli in una mappa. Il codice che scriveremo sarebbe questo:

Map<String,Utente> m = new HashMap<>();

Per inserire un oggetto scriveremo: 

	m.put("UtenteGiovanni", u);

Dove u è un oggetto di tipo utente.

 

Gli insiemi (HashSet)

Un insieme è un tipo di dato che non ammette duplicati, ovvero non possiamo inserire più di una volta la stessa chiave. Sugli insiemi possiamo eseguire le operazioni dell’insiemistica, ovvero l’intersezione, l’unione e cosi via. Per creare un insieme dobbiamo importare queste classi:

import java.util.Set;

import java.util.HashSet;

Creiamo il nostro primo insieme, che conterrà delle stringhe:

Set set = new HashSet<>();

Aggiungiamo i nostri primi elementi:

	set.add("a");

set.add("b");

Nota: gli insiemi non accettano duplicati. Se inseriamo ad esempio questo codice:

	set.add("c");

set.add("c");

nell’insieme troveremo comunque un solo elemento “c”.

 

Operazioni con gli insiemi

Per eseguire l’intersezione di due insiemi dobbiamo usare il metodo retainAll().

	set.retainAll(set2);

Questo metodo va invocato su un insieme e ha bisogno di un altro insieme come parametro ed eliminerà nell’insieme su cui è stato invocato tutti gli elementi che non fanno parte di set2.

Dati questi insiemi:

set1 = [A, B, C]

set2 = [A, D]

l’esecuzione del metodo retainAll con questi due insiemi porterà a questo insieme:

risultato = [A]

Per eseguire l’unione usiamo invece il metodo addAll()

	set.addAll(set2);

questo metodo aggiunge il contenuto dell’insieme passato come parametro all’interno dell’insieme su cui è invocato. Ovviamente se ci sono duplicati non troveremo due volte lo stesso valore.

Dati questi insiemi:

set1 = [A, B, C]

set2 = [A, D]

l’unione di questi insiemi porterà ad avere questo nuovo insieme:

risultato = [A, B, C, D]

 

Le code

Una coda (queue in inglese) è una struttura dati che si comporta proprio come una coda ad una cassa o ad un ufficio. I dati inseriti al suo interno sono ordinati in ordine di arrivo e verranno eliminati nello stesso ordine. Questo approccio si definisce FIFO (first in first out), ovvero il primo elemento che viene inserito nella coda sarà il primo ad uscire. Le code vengono utilizzate quando il programma che dobbiamo realizzare richiede di svolgere alcune operazioni in ordine di arrivo. Si pensi ad esempio ad un programma che gestisce le richieste di assistenza dei clienti: un tecnico che si libera deve sapere qual è la prossima richiesta di assistenza a cui deve lavorare. Questa situazione potrebbe essere gestita con una coda

 

Diversi tipi di code

Il tda coda cosi come descritto sopra è solo una base, sono stati sviluppati diversi tipi di code adatti a diverse esigenze. Vediamone le principali:

  • Coda a priorità
    Si tratta di una coda che aggiunge, all’approccio FIFO, una variante legata alla priorità di un elemento inserito.
  • Coda sincronizzata
    Si tratta di un tipo di coda utilizzato nella programmazione concorrente, ovvero quando più processi di un programma accedono contemporaneamente alla coda.

Esistono molti altri tipi di code, ma per i nostri fini didattici utilizzeremo un’implementazione delle code a priorità chiamata PriorityQueue, che in caso di priorità tutte uguali si comporterà esattamente secondo il principio FIFO, altrimenti terrà conto delle priorità.

		PriorityQueue queue = new PriorityQueue<>();	

Aggiungere un elemento nella coda

		queue.add("Lucia");

 

Rimuovere il prossimo elemento nella coda

Per rimuovere l’elemento primo in lista nella coda si usa il metodo remove. Il metodo restituisce l’oggetto rimosso  in una variabile.

		String elementoRimosso = queue.remove();

 

Conoscere il primo elemento della coda

In alcuni casi potremmo sapere qual è il prossimo elemento della coda, senza però rimuoverlo. In questi casi possiamo usare il metodo peek:

		String elemento = queue.peek();	

// conoscere la dimenzione di una coda

int size = queue.size();

 

Gli stack

Una pila (stack in inglese) è una struttura dati che si comporta in maniera opposta alla coda: il primo elemento ad essere inserito sarà il primo ad essere restituito. Immaginiamo una pila di libri: mettiamo l’uno sull’altro diversi libri, quando dobbiamo riprenderli per ordinarli il libro in cima sarà l’ultimo libro che abbiamo posto su di essa. Java mette a disposizione la classe Stack per implementare questa struttura dati.

 

Creiamo il nostro primo stack:

Come per gli altri elementi del Java Collection Framework dobbiamo specificare il tipo di dato, nel nostro caso String.

	Stack stack = new Stack<>();

 

Aggiungere elementi

Per aggiungere un elemento allo stack utilizziamo il metodo add:

	stack.add("Luca");

possiamo anche decidere dove inserirlo, specificando una posizione

	stack.add(2, "Antonio");

 

Rimuovere elementi

Per rimuovere elementi dallo stack si usa il metodo remove. Questo metodo richiede di specificare la posizione da rimuovere, questo ci consente di seguire la logica degli stack quando questa ci serve, mettendo come indice 0:

	String elementoRimosso = stack.remove(0);

Possiamo però decidere di non seguire questa logica e rimuovere un qualsiasi altro elemento.

 

Gli iteratori

Vediamo ora un altro importante elemento del java collection framework, la classe Iterator. Questa classe consente di iterare tutti gli elementi di una qualsiasi classe del java collection framework

Dispone di un metodo hasNext per sapere se ci sono ancora elementi, e del metodo next per recuperare il prossimo elemento.

Nota che l'iteratore non consente di tornare indietro, quando un elemento viene restituito non può essere richiesto nuovamente. L'iteratore invia tutti gli elementi una volta sola, quando restituisce l'ultimo elemento si è esaurito. Ne consegue che gli iteratori possono essere usati una volta sola.

Supponiamo di avere una variabile set che è un insieme contenente questi valori: [a, b, c, d, e, f, g, h, i]

possiamo creare un iteratore di questo insieme in questo modo

	Iterator it = set.iterator();

Come gli altri elementi del Java Collection Framework anche per gli iteratori bisogna specificare il tipo di dato tra i simboli <>. Il tipo di dato deve ovviamente coincidere con quello della classe su cui si invoca l'iteratore. Per scorrere tutti gli elementi dell’iteratore usiamo:

		 while(it.hasNext()) {

String elem = it.next();

System.out.println(elem);

}

Nell’esempio qui sopra stampiamo l’elemento, ma potremmo compiere le operazioni più disparate. 

L'iteratore può essere invocato su liste, set, code e altri elementi del java collection framework, ma non si può invocare direttamente su una mappa, perché è una collezione di elementi nome - valore. Per usarlo sulla mappa invochiamo il metodo keyset e che restituisce un insieme delle chiavi e poi invochiamo su di esso l'iteratore. Vediamo un esempio qui sotto, supponiamo di avere una mappa m con un certo numero di elementi.

		 Iterator it = m.keySet().iterator();

while(it2.hasNext()) {

String elem = it.next();

System.out.println(m.get(elem));

}
Leggi tutto

Andrea Pastore 19/03/2020

12. Polimorfismo in Java

Alcuni linguaggi di programmazione consentono di decidere il comportamento dei metodi e costruttori in base al numero di variabili che vengono passate loro come parametro.  Supponiamo ad esempio di avere una classe Java chiamata Utente, con tre variabili (id, nome, cognome) e due costruttori: il primo prenderà in input solo una variabile id, il secondo prenderà in input una variabile id, una variabile nome e una variabile cognome.

I due costruttori in java saranno i seguenti

public Utente(int id) {

this.id = id;

this.nome = “”;

this.cognome = “”;

this.email = “”;

}

public Utente(int id, String nome, String cognome) {

this.id = id;

this.nome = nome;

this.cognome = cognome;

}

Quando noi invochiamo il costruttore a seconda del numero di parametri che inseriamo Java userà il costruttore che combacia con i parametri inseriti ed eseguirà il codice contenuto all’interno. Java darà un errore solo se non trova un costruttore con il numeor di parametri da noi specificato. Ad esempio se scriviamo:

	Utente utente =  new Utente(3,”Mario”,”Rossi”,”mario@gmail.com”);

Otterremo un errore, perché non abbiamo definito un costruttore che prende in input quattro parametri. Tutto quello che abbiamo detto per i costruttori è valido anche per i metodi (del resto il costruttore non è altro che un metodo speciale). Prendiamo in esame il seguente metodo che stampa tutti i dati dell’utente:

public void stampaDati() {

System.out.println(this.id + “ ” + this.nome + “ ” + this.cognome + “ ” +this.email);

}

Come per i costruttori possiamo creare un altro metodo che ha lo stesso nome, ma prende in input un parametro per stabilire come devono essere stampati questi elementi. Stabiliamo due tipologie di stampa: essenziale che stampa solo nome e cognome, completa che stampa tutto. Se il parametro passato in input non corrisponde a nessuna di queste due cose allora questo metodo invoca a sua volta il metodo stampaDati senza parametri, che stamperà i dati senza formattazione.

	public void stampaDati(String tipoVisualizzazione) {

if(tipoVisualizzazione.equals("essenziale")) {

System.out.println("Nome:"+this.nome+"; Cognome:"+this.cognome);

}

else if (tipoVisualizzazione.equals("completa")){

System.out.println("Id: "+this.id + "; Nome:"+this.nome+"; Cognome:"+this.cognome+"; Email:"+this.email);

}

else {

stampaDati();

}

}

Ereditarietà

L’ereditarietà è una caratteristica di molti linguaggi di programmazione moderni che consente di ottimizzare il codice scritto ed evitare ripetizioni. Ci sono casi in cui dobbiamo creare classi molto simili tra loro. Per questo molti linguaggi di programmazione consentono di usare il meccanismo dell’ereditarietà. Supponiamo di avere bisogno di questa due classi:

  • Utente
    id, nome, cognome, email,telefono
  • Fornitore
    id, nome, cognome, email,telefono,partitaIva

notiamo che Fornitore è quasi uguale alla classe Utente, quindi non ha senso riscriverla. Quando creiamo la classe Fornitore che estenderà la classe Utente. Scriviamo le due classi per renderci conto del codice e delle differenze tra le due. Qui sotto vediamo la classe utente:

public class Utente {

private int id ;

private String nome;

private String cognome;

private String email;

private String telefono;



public Utente(int id, String nome, String cognome, String email, String telefono) {

this.id = id;

this.nome = nome;

this.cognome = cognome;

this.email = email;

this.telefono = telefono;

}

// Getter & setter

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

… tutti gli altri getter e setter

}

Vediamo ora la classe Fornitore. Come abbiamo capito Java ci consente di evitare di ricopiare la classe, basta dichiarare, al momento della creazione, che la nuova classe estende la classe Utente. Questo avviene con la parola chiave extends.

public class Fornitore extends Utente {

private String partitaIva;

public Fornitore(String partitaIva, int id, String nome, String cognome, String email, String telefono) {

super(id, nome, cognome, email, telefono, via, citta, cap);

this.partitaIva = partitaIva;

}

public String getPartitaIva() {

return partitaIva;

}

public void setPartitaIva(String partitaIva) {

this.partitaIva = partitaIva;

}

}


Notiamo alcune differenze con la classe Utente scritta sopra: innanzitutto vediamo che nel costruttore c’è la parola chiave super, seguita da una serie di parametri. Si tratta dei parametri del costruttore della classe Utente, che viene richiamato con il comando super. Ogni volta che noi estendiamo una classe il costruttore della nuova classe deve richiamare il costruttore della classe che estende. Fornitore estende la classe Utente, quindi il suo costruttore prende in input anche i parametri che richiede il costruttore di Utente, che invocherà con il metodo super().

Nota: quando una classe estende un’altra classe, la classe estesa è definita superclasse

 

Le interfacce in Java

Vediamo ora un altro concetto fondamentale della programmazione: le interfacce, che  servono per standardizzare il codice.

 

Interfacce

Le interfacce vengono utilizzate per definire un tipo di dato e una classe, che però non viene implementato. Vengono definite solo le firme dei metodi che poi saranno implementate in altre classi. Vediamo un esempio di interfaccia:

public interface Libro {

public void stampaLibro();

public void setTitolo(String titolo);

public String getTitolo();

public void setNumeroPagine(int numeroPagine);

public int getNumeroPagine();

}

Per usare una determinata interfaccia dopo il nome della classe dobbiamo usare la parola chiave implements seguita dal nome dell'interfaccia. Appena facciamo questo il nostro ide ci segnalerà che i metodi dell'interfaccia non sono ancora implementati, clicchiamo su add unimplementated methods (potrebbe cambiare a seconda dell’ide usato) e il nostro ide li aggiungerà per noi, dobbiamo solo completarli con il codice all'interno.

In sostanza le interfacce consentono di standardizzare il codice: dueprogrammatori si accordano sui metodi e su come vengono chiamati, e poiai fini dell'interoperabilità delle classi non è importante come vieneimplementato. Nel tempo le implementazioni possono cambiare, ma rispettandole interfacce non ci saranno problemi di incompatibilità

 

Il paradigma MVC

La programmazione ad oggetti è utilizzata nel paradigma MVC (MODEL, VIEW, CONTROLLER), che consente una separazione delle logiche. Vediamo i tre elementi nel dettaglio:

  • model
    sono le classi cosi come le abbiamo viste. Quando creiamo una classe infatti definiamo degli oggetti ma non li usiamo in quell’istante
  • view
    le viste rappresentano la parte del progetto che mostra i dati visivamente
  • controller
    sono i file che consentono di rendere permanenti le operazioni sugli oggetti: creazione di nuovi elementi, modifica di elementi esistenti, eliminazione di elementi e anche recupero dei dati di un elemento. I controller nascondono le logiche sottostanti alle altre classi: potremmo voler salvare i dati in un file oppure nel database, per le classi esterne viene chiamato lo stesso metodo. Facendo passare tutte le operazioni di salvataggio, modifica, caricamento per il controller avremo solo una classe che gestisce queste operazioni, che si traduce in un vantaggio per la manutenibilità del codice, in quanto per apportare modifiche alla gestione degli oggetti dovremmo modificare solo la classe relativa.
Leggi tutto

Andrea Pastore 19/03/2020

18. Programmazione concorrente

Prima di introdurre questo agomento dobbiamo capire come funzionano i moderni processori: vediamo la foto di un’architettura di un processore intel:

Vediamo che è composto da varie parti: i core (le unità che di elaborazione, il codice che scriviamo viene elaborato da loro) una piccola memoria interna (da non confondersi con la memoria RAM, che non risiede nel processore) e poi una scheda video. Il numero di processi che può essere eseguito contemporaneamente dipende dal numero di core, quindi il processore mostrato in figura può eseguire quattro processi contemporaneamente. Il sistema operativo ha il compito di assegnare le risorse ad un processo: tra le altre cose deve decidere a quale processore assegnare il processo, controllare che non si blocchi, aggiornare la coda dei processi in attesa di essere eseguiti e molto altro ancora. Un processo resta in esecuzione per una frazione di secondo, poi se ha esaurito il compito viene terminato, altrimenti viene reinserito nella coda dei processi che in attesa di essere eseguiti.

I programmi moderni, a meno che non siano programmi molto piccoli sono composti da tanti processi in esecuzione sul proprio computer, ognuno con un compito. Un esempio è rappresentato dai browser web, come Firefox: l’architettura di questo browser prevede attualmente che ci sia un processo deputato a mostrare l’interfaccia (che mostra ad esempio le tab aperte) e altri processi che gestiscono le tab.

 

Lo scheduler del sistema operativo

Per stabilire quale processo debba essere eseguito ogni sistema operativo dispone di un software chiamato scheduler, che gestisce la coda dei processi tenendo conto di una serie di fattori: priorità, stato del processo e molti altri. Se il sistema operativo è progettato per gestire hardware in grado di gestire più thread contemporaneamente (ovvero tutti gli strumenti che utilizziamo maggiormente oggi: computer, telefonini, ma anche centraline di oggetti di uso comune come l’automobile) il suo ruolo si complica perché deve tenere conto anche degli altri thread. Ad esempio un thread di un processo potrebbe dover aspettare che un altro thread termini l’esecuzione.

 

La programmazione concorrente in Java

Java mette a disposizione due modi per creare processi concorrenti: la prima possibilità consiste nella creazione di una classe che estende la classe Thread, la seconda consiste nella creazione di una classe che implementa l’interfaccia Runnable. Le differenze dal punto di vista del codice sono minime, creiamo due classe Trhead_A e Trhead_B per vederle:

 

Classe che estende la classe Thread

public class Thread_A extends Thread {

private int contatore;



public Thread_A() {

this.contatore = 0;

}



public void run() {

this.contatore ++;

System.out.println("Trhead A: contatore= "+contatore);

}

}

 

Classe che implementa l’interfaccia Runnable

public class Thread_B implements Runnable{

private int contatore;

public Thread_B() {

this.contatore = 0;

}

@Override

public void run() {

this.contatore++;

System.out.println("Thread B: contatore="+contatore);

}

}

Notiamo che entrambe le classi hanno un metodo public void run(). Questo è un metodo necessario in quanto descrive le operazioni che deve fare il Thread. Nei nostri esempi qui sopra i thread incrementano un contatore e lo stampano.

 

Lanciare un Thread

A seconda del metodo che abbiamo scelto, i Thread si lanciano in due modi diversi. Se abbiamo scelto di creare le classi che estendono la classe Thread, come nel nostro esempio del Trhead_A, scriveremo:

Thread_A t  = new Thread_A();

t.start();

Se usiamo una classe che estende l’interfaccia Runnable creeremo prima un’istanza di quella classe, e un Trhead a partire da quella:

	Thread_B rc = new Thread_B();

Thread t1 = new Thread(rc);

t1.start();

Nota: in entrambi i casi per far partire un thread bisogna invocare il metodo start().

 

Assegnare la priorità ad un thread

È possibile impostare la priorità di un thread con il metodo setPriority. Java prevede per la priorità un valore da 1 a 10, se non lo impostiamo manualmente la priorità assegnata di default è 5. La classe Trhead dispone di tre costanti che sono:

MAX_PRIORITY = 10

MIN_PRIORITY = 1

MEDIUM_PRIORITY  = 5

Se ad esempio voglio impostare la priorità massima scriverò:

	t.setPriority(Thread.MAX_PRIORITY);

Per impostare altri valori sulla scala da 1 a 10 possiamo usare i numeri.

 

Mettere in pausa un thread

Può capitare di dover fermare un thread per un certo intervallo di tempo (ad esempio perché deve ripetere una determinata operazione dopo un tempo prestabilito), per questo Java mette a disposizione diversi metodi. Sleep, join e yeld.

 

sleep

prende in input il numero di millisecondi durante i quali il thread deve stare in pausa. Per invocarlo correttamente bisogna utilizzare il costrutto try .. catch, perché dobbiamo prevenire il fatto che un altro thread provi a chiamare il thread dove abbiamo invocato il metodo sleep:

	try {       	  		   

t1.sleep(10);

     } catch (InterruptedException ex) {

       // handle the error

       System.out.println("si è verificata un'eccezione");

     }

 

join

questo metodo serve a mettere in attesa il thread su cui è invocato fino a quando altri trhead al suo interno non abbiano terminato l’esecuzione. Supponiamo di avere un thread t1 che crea un thread t2 per eseguire un’operazione. L’istruzione:

	t2.sleep(10);

metterà in attesa il thread t1 fino a quando t2 non avrà terminato la sua esecuzione.

 

yeld

È un metodo statico che suggerisce allo scheduler di mettere in pausa il tread corrente per lasciare spazio ad altri. Viene invocato in questo modo:

	Thread.yield();
Leggi tutto

Cerca...