Il modo più classico (anche se rigido) per creare un processo parallelo.
class MioThread extends Thread {
public void run() {
System.out.println("1. Ciao dal thread: " + getName());
}
}
public class Base1 {
public static void main(String[] args) {
new MioThread().start(); // Avvia il thread, non chiamare run()!
}
}L'approccio preferito in Java, perché lascia la classe libera di estendere altre cose.
public class Base2 {
public static void main(String[] args) {
Runnable compito = () -> System.out.println("2. Lavoro tramite Runnable");
new Thread(compito).start();
}
}Possiamo "suggerire" alla JVM chi è più importante (da 1 a 10). La JVM ha comunque l'ultima parola.
public class Base3 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> System.out.println("Thread Basso"));
Thread t2 = new Thread(() -> System.out.println("Thread Alto"));
t1.setPriority(Thread.MIN_PRIORITY); // 1
t2.setPriority(Thread.MAX_PRIORITY); // 10
t1.start(); t2.start();
}
}Un thread dice: "Ho fatto abbastanza per ora, cedo volentieri la CPU ad altri thread di pari priorità".
public class Base4 {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("Inizio lavoro lungo...");
Thread.yield(); // Metto in pausa me stesso per far passare altri
System.out.println("Riprendo e finisco!");
}).start();
}
}Il thread si addormenta per un tempo millesimale esatto.
public class Base5 {
public static void main(String[] args) throws InterruptedException {
System.out.println("Inizio attesa...");
Thread.sleep(2000); // Il main dorme per 2 secondi
System.out.println("Sono passati 2 secondi!");
}
}Il thread principale aspetta che un altro abbia finito prima di proseguire.
public class Base6 {
public static void main(String[] args) throws InterruptedException {
Thread operaio = new Thread(() -> System.out.println("Operaio: Finito!"));
operaio.start();
operaio.join(); // Il Main aspetta qui
System.out.println("Main: L'operaio ha finito, chiudo il cantiere.");
}
}Ecco i 4 esercizi finali, ottimizzati per essere leggibili e focalizzati sull'obiettivo.
Obiettivo: Due thread, uno conta da 1 a 10, l'altro da 100 a 50 su colonne diverse (usando i tabulati \t).
public class Ex1_Colonne {
public static void main(String[] args) {
// Colonna Sinistra (1 -> 10)
new Thread(() -> {
for(int i=1; i<=10; i++) System.out.println(i + "\t");
}).start();
// Colonna Destra (100 -> 50)
new Thread(() -> {
for(int i=100; i>=50; i--) System.out.println("\t\t" + i);
}).start();
}
}Obiettivo: Simulare un cliente che ordina e aspetta 5 secondi la preparazione della pizza.
public class Ex2_Pizzeria {
public static void main(String[] args) throws InterruptedException {
System.out.println("Cliente: Ordino la margherita e aspetto...");
Thread pizzaiolo = new Thread(() -> {
try {
System.out.println("Pizzaiolo: Preparazione in corso (5s)...");
Thread.sleep(5000); // Cottura
System.out.println("Pizzaiolo: Pizza pronta!");
} catch (InterruptedException e) {}
});
pizzaiolo.start();
pizzaiolo.join(); // Il cliente aspetta che il pizzaiolo finisca
System.out.println("Cliente: Grazie, finalmente mangio!");
}
}Obiettivo: Buffer da 1 posto. Produttore genera 1-10. Consumatore A mangia solo 1-5, Consumatore B mangia solo 6-10.
public class Ex3_ProdCons {
static class Buffer {
int dato = 0;
boolean pieno = false;
// Metodo per il PRODUTTORE
synchronized void produci(int n) throws InterruptedException {
while (pieno) wait(); // Se è pieno, aspetto
dato = n;
pieno = true;
System.out.println("Prodotto: " + n);
notifyAll(); // Avviso i consumatori
}
// Metodo per i CONSUMATORI (A accetta 1-5, B accetta 6-10)
synchronized void consuma(boolean vuoleBassi) throws InterruptedException {
// Condizione di attesa: o è vuoto, o c'è un numero che non mi piace
while (!pieno || (vuoleBassi && dato > 5) || (!vuoleBassi && dato <= 5)) {
wait();
}
System.out.println((vuoleBassi ? "Cons. Basso" : "Cons. Alto") + " mangia: " + dato);
pieno = false;
notifyAll(); // Avviso il produttore che ho liberato il posto
}
}
public static void main(String[] args) {
Buffer b = new Buffer();
new Thread(() -> { // Produttore
try { for(int i=1; i<=10; i++) b.produci((int)(Math.random()*10)+1); } catch(Exception e){}
}).start();
new Thread(() -> { // Consumatore Basso (1-5)
try { while(true) b.consuma(true); } catch(Exception e){}
}).start();
new Thread(() -> { // Consumatore Alto (6-10)
try { while(true) b.consuma(false); } catch(Exception e){}
}).start();
}
}Obiettivo: 10 auto, 5 posti. Attesa massima in coda 20 secondi. Sosta massima 10 secondi. Segnali di wait e notify.
public class Ex4_Parcheggio {
static class Parcheggio {
int postiLiberi = 5;
synchronized boolean entra() throws InterruptedException {
long inizioAttesa = System.currentTimeMillis();
// Finché non ci sono posti, aspetto...
while (postiLiberi == 0) {
long tempoPassato = System.currentTimeMillis() - inizioAttesa;
if (tempoPassato >= 20000) return false; // Scaduti i 20 sec, me ne vado!
// Aspetto il tempo che mi rimane per arrivare a 20 sec
wait(20000 - tempoPassato);
}
postiLiberi--; // Posto preso
return true;
}
synchronized void esci() {
postiLiberi++; // Libero il posto
notifyAll(); // Avviso le auto in coda!
}
}
public static void main(String[] args) {
Parcheggio p = new Parcheggio();
for (int i = 1; i <= 10; i++) {
Thread auto = new Thread(() -> {
String nome = Thread.currentThread().getName();
try {
System.out.println(nome + " cerca parcheggio...");
if (p.entra()) {
System.out.println(nome + " HA PARCHEGGIATO.");
Thread.sleep((long)(Math.random() * 10000)); // Sosta fino a 10 sec
p.esci();
System.out.println(nome + " è uscita.");
} else {
System.out.println(nome + " RINUNCIA: attesa oltre 20s.");
}
} catch (InterruptedException e) {}
});
auto.setName("Auto-" + i);
auto.start();
}
}
}