import java.util.stream.IntStream;
public class FizzBuzz {
public static void main(String[] args) {
IntStream.rangeClosed(1, 100)
.mapToObj(i -> (i % 15 == 0) ? "FizzBuzz" : (i % 3 == 0) ? "Fizz" : (i % 5 == 0) ? "Buzz" : i)
.forEach(System.out::println);
}
}Questa è una sfida interessante. Di solito i thread sono fatti per correre in parallelo, ma qui vogliamo l'esatto opposto: una coordinazione millimetrica per mantenere l'ordine sequenziale.
Per scrivere meno codice possibile, la strategia migliore è usare un unico oggetto di sincronizzazione e un indice condiviso. Invece di creare classi complesse, useremo delle espressioni lambda.
Ecco una versione super compatta in Java:
public class FizzBuzzThreads {
private static int counter = 1;
private static final Object lock = new Object();
public static void main(String[] args) {
Runnable task = () -> {
while (true) {
synchronized (lock) {
if (counter > 100) return;
String out = (counter % 3 == 0 ? "Fizz" : "") + (counter % 5 == 0 ? "Buzz" : "");
System.out.println(out.isEmpty() ? counter : out);
counter++;
lock.notifyAll();
try { if (counter <= 100) lock.wait(); } catch (InterruptedException e) {}
}
}
};
new Thread(task).start();
new Thread(task).start();
}
}- Sincronizzazione minima: Usiamo un unico blocco
synchronizedsu un oggettolock. - Logica condivisa: Entrambi i thread eseguono lo stesso
Runnable. Non serve scrivere logiche separate per "chi fa cosa". - Passaggio del testimone: Con
lock.notifyAll()elock.wait(), i thread si danno il cambio in modo educato. Appena uno finisce di stampare e incrementare, sveglia l'altro e si mette in pausa. - Concatenazione di stringhe: L'uso degli operatori ternari
(counter % 3 == 0 ? "Fizz" : "")è il modo più breve in Java per gestire le condizioni del FizzBuzz senza una sfilza diif-else.
- Thread A entra nel blocco, stampa "1", incrementa il contatore a 2.
- Thread A sveglia tutti e va in
wait(). - Thread B si sveglia, stampa "2", incrementa a 3, sveglia tutti e va in
wait(). - Il ciclo continua fino a 100, dove il
returninterrompe l'esecuzione.
Nota: Se volessi essere ancora più estremo e usare 4 thread (uno per Fizz, uno per Buzz, uno per FizzBuzz e uno per i numeri), il codice triplicherebbe perché dovresti gestire condizioni di attesa molto più specifiche. Questa versione a 2 thread è il "punto dolce" tra multithreading e brevità.
Ti piacerebbe vedere come cambierebbe la struttura se volessimo usare esplicitamente un thread dedicato solo a "Fizz" e uno solo a "Buzz"?