Continuiamo a vedere come utilizzare i thread in Java. Se non hai letto la prima lezione, puoi trovarla a questo link. Questa volta ci addentriamo in un argomento più delicato. Come possiamo far interagire due o più thread sullo stesso spazio di memoria condiviso in modo concorrente?
Vediamo due modalità: utilizzo dei vector e utilizzo degli arraylist. Utilizziamo queste strutture dati e ci poniamo di realizzare una sorta di gioco del tris, anche se non è esattamente a turni come nel gioco dei bimbi. Vogliamo costruire un vettore di 9 caselle che simuli la famosa tabella di gioco ed ognuno tra due thread che piazza in un tempo casuale un simbolo X o O.
Indice dei contenuti
La struttura dati Vector
Il vector, qualche lettore potrebbe già stranirsi, è una struttura dati molto versatile del Java, peccato si obsoleta e dalla versione JDK 8, viene mantenuto un porting di retrocompatibilità e ne viene sconsigliato l’utilizzo in nuovi progetti di produzione. In realtà il vector è una struttura dati molto didattica e si presta molto bene a semplificare le trattazione della concorrenza tra thread java. Infatti allo studente non occorre sincronizzare letture o scritture su uno spazio di memoria comune in quanto la progettazione della struttura dati è già thread-safe e non sono necessarie diciture o metodi di sincronizzazione.
Essendo didattica, molti docenti di scuola superiore o universitari rimangono ancorati a questa trattazione, che svisceriamo quindi anche noi. Però mi sento in dovere di sconsigliare categoricamente l’uso di vector per applicazioni commerciali vere! Nei codici moderni si preferiscono CopyOnWriteArrayList o la ConcurrentHashMap.
La classe thread Giocatore
Per il nostro thread, prevediamo un attributo per gestire il simbolo X o O ed un buffer comune che andiamo a dichiarare di tipo vector ma statico. Questo è fondamentale. Se il buffer non fosse statico, ogni thread avrebbe una sua versione/copia mentre così ce n’è uno per tutti. Il restante codice no ha nessuna particolarità. Nel costruttore facciamo inizializzare una volta per tutti i thread in gioco, il campo di gioco, la matrice 3×3 che qui simuliamo con il vettore. A questo punto, il thread non deve fa altro che cercare finché ci sono spazi vuoti o, meglio, col simbolo di default -, pescare un numero a caso da 0 a 9, provare a piazzare il proprio simbolo, e attendere in ogni caso, piazzato o meno, un tempo casuale, qui ad esempio impostato entro i 2000 millisecondi.
Abbiamo aggiunto un metodo toString() per stampare la matrice una volta che è tutta piena e i due thread sono rientrati a quello principale.
package iisteramo.tris.thread;
import java.util.Random;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author alfredo cetinaro
*/
public class Giocatore extends Thread
{
private char simbolo;
public static Vector<Character> vettore;
public Giocatore(char _simbolo)
{
this.simbolo = _simbolo;
if (this.vettore == null)
{
this.vettore = new Vector<>();
for (int i=0; i < 9; i ++)
{
vettore.add('-');
}
}
}
@Override
public void run()
{
int posizione;
Random rand = new Random();
while(this.vettore.contains('-'))
{
posizione = rand.nextInt(9) ;
if (this.vettore.get(posizione) == '-')
{
this.vettore.set(posizione, simbolo);
System.out.println("posizione:" + posizione + " simbolo: " + simbolo);
}
try {
Thread.sleep(rand.nextInt(2000));
} catch (InterruptedException ex) {
Logger.getLogger(Giocatore.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@Override
public String toString()
{
String tris = this.vettore.get(0) + " " + this.vettore.get(1) + " " + this.vettore.get(2) + "\n"
+ this.vettore.get(3) + " " + this.vettore.get(4) + " " + this.vettore.get(5) + "\n"
+ this.vettore.get(6) + " " + this.vettore.get(7) + " " + this.vettore.get(8);
return tris;
}
}La classe di test/prova
Il metodo di test/main in realtà è molto semplice. Inizializzo i due thread con simboli differenti, li lancio e attendo il loro ricongiungimento al programma principale, stampo la matrice riempita. Mi attendo che non ci siano tanti X e O a metà. E’ probabile che uno dei thread dorma di meno e piazzi più simboli.
package iisteramo.tris.thread;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author alfredo centinaro
*/
public class TrisThread {
public static void main(String[] args)
{
Giocatore g1 = new Giocatore('X');
Giocatore g2 = new Giocatore('O');
g1.start();
g2.start();
try {
g1.join();
} catch (InterruptedException ex) {
Logger.getLogger(TrisThread.class.getName()).log(Level.SEVERE, null, ex);
}
try {
g2.join();
} catch (InterruptedException ex) {
Logger.getLogger(TrisThread.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.print(g2.toString());
}
}Ultima modifica 8 Agosto 2025

