Prima che ci fosse il pacchetto java.util.concurrent
, che è atterrato nella versione Java 5, non c'erano altre opzioni per eseguire calcoli simultanei, ma per manipolare direttamente i valori di Thread
s.
È possibile manipolare direttamente i fili, ad es. sottoclasse:
public class MyThread extends Thread {
public void run() {
myComputation();
}
}
Oppure si potrebbe fare lo stesso con la creazione di un Runnable
, che era il modo preferito (soprattutto perché non è necessario creare una sottoclasse nulla, che prevede una netta separazione di preoccupazione, codice migliore riutilizzo o in generale , una composizione migliore):
public class MyRunnable implements Runnable {
public void run() {
mycomputation();
}
}
new Thread(new MyRunnable()).start();
Ma non importa in che modo si è utilizzato, era necessario creare, lancio, manipolare, unirsi sui filetti. Che ha degli svantaggi: quanti thread puoi creare? È costoso questo? (Ad un certo punto lo è, anche se è diventato più economico ed economico con ogni implementazione JVM.)
Inoltre, questa API stessa presenta limitazioni che lo sviluppatore ha dovuto superare: cosa succede se voglio restituire un valore da un Runnable
, vedere la firma non lo consente comunque? Come posso sapere se il mio Runnable
non è riuscito con un Exception
? Ci sono cose come i gestori di eccezioni e simili per consentire ciò con i thread, ma era un compito ripetitivo, incline agli errori.
Inserisce il pacchetto java.util.concurrent
! Offre una risposta a tutto questo (e altro)!
Si desidera riutilizzare i thread per più di una "unità di lavoro" (sia esso un Runnable
o no?): È possibile. Vuoi sapere se l'unità di lavoro è stata completata o fallita: puoi farlo. Vuoi restituire un valore: puoi. Vuoi dare priorità dinamiche ad alcuni compiti rispetto ad altri? Sicuro. Hai bisogno della possibilità di pianificare le attività? Può fare. E così via.
sostituire il vostro Runnable
s con Callable
s (questo è semplice come può essere, entrambi sono interfacce singolo metodo!), Smettere di manipolare Thread
s in favore di Future
s e lasciare l'impianto idraulico a ExecutorService
.
Il mio dubbio è se Callable è in grado di fare tutto ciò che è eseguibile, perché così tante persone usano Runnable invece di callable?
Forse hanno un vecchio codice (Java 1.4?). Forse non sono a conoscenza dei vantaggi delle astrazioni di livello superiore e preferiscono le cose di livello basso Thread
?
In generale, non vi è assolutamente alcun motivo per preferire l'utilizzo di Thread
s dall'API simultanea.
Ci sono spese generali aggiuntive nell'implementazione dell'interfaccia Callable rispetto a Runnable Interface?
In "attuazione Callable
" vs Runnable
, non c'è nessuno. Ma c'è un costo per il nuovo materiale Callable
perché internamente, tutto torna a Thread
se Runnable
s (almeno, le attuali implementazioni lo fanno). Ma potresti effettivamente ottenere prestazioni, grazie a nuove funzionalità come il più facile riutilizzo dei thread!
Quindi, sì, l'API simultanea ha il suo costo, ma è assolutamente, completamente trascurabile in qualsiasi caso d'uso standard, ma ha anche nuovi punti di forza che lo rendono migliore in molti modi, comprese le prestazioni.
Inoltre, si ottengono enormi nuove possibilità dall'API, come già affermato (consultare il framework Fork/Join, vedere i flussi paralleli in Java 8, ...) e ridurre la necessità di qualsiasi nodo di "personalizzato" , codice di concorrenza "autoprodotto" per dette funzionalità, che è notoriamente difficile da ottenere.
Il rapporto costi/benefici favorisce completamente la "nuova" API concorrente.
Non si dovrebbe usare Callable, se non è necessario. Come afferma [principio KISS] (https://people.apache.org/~fhanik/kiss.html). –