window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = finestra if (w.LeadBooster) { console.warn('LeadBooster esiste già') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Concorrenza in Java Parte 1 - Introduzione - The Codest
The Codest
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Industrie
    • Fintech e banche
    • E-commerce
    • Adtech
    • Tecnologia della salute
    • Produzione
    • Logistica
    • Automotive
    • IOT
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
Freccia indietro TORNA INDIETRO
2022-06-15
Sviluppo di software

Concorrenza in Java Parte 1 - Introduzione

The Codest

Rafal Sawicki

Sviluppatore Java

Leggete la prima parte della nostra serie di blog dedicata alla concorrenza in Java. Nel prossimo articolo daremo uno sguardo più approfondito alle differenze tra thread e processi, ai pool di thread, agli esecutori e a molto altro ancora!

In generale, l'approccio convenzionale alla programmazione è sequenziale. Tutto in un programma avviene un passo alla volta.
Ma, in realtà, il parallelo è il modo in cui funziona il mondo intero: è la capacità di eseguire più di un compito contemporaneamente.

Filo e processo

Per discutere di argomenti avanzati come concomitanza in Java o multithreading, dobbiamo trovare delle definizioni comuni per essere sicuri di essere sulla stessa lunghezza d'onda.

Cominciamo con le basi. Nel mondo non sequenziale, abbiamo due tipi di rappresentazioni della concorrenza: processi e
thread. Un processo è un'istanza del programma in esecuzione. Normalmente, è isolato dagli altri processi.
Il sistema operativo è responsabile dell'assegnazione delle risorse a ciascun processo. Inoltre, agisce come un conduttore che
programma e controlla.

Il thread è una sorta di processo ma a un livello inferiore, pertanto è noto anche come thread leggero. Più thread possono essere eseguiti in un
processo. In questo caso il programma agisce come scheduler e controllore dei thread. In questo modo i singoli programmi sembrano fare
più compiti contemporaneamente.

La differenza fondamentale tra thread e processi è il livello di isolamento. Il processo ha un proprio insieme di
mentre il thread condivide i dati con altri thread. Può sembrare un approccio a rischio di errore, e in effetti lo è. Per
Ora, non concentriamoci su questo aspetto perché esula dallo scopo di questo articolo.

Processi, thread - ok... Ma cos'è esattamente la concorrenza? Concorrenza significa che è possibile eseguire più attività allo stesso tempo.
tempo. Non significa che questi compiti debbano essere eseguiti simultaneamente: questo è il parallelismo. Concurrenc in Javay inoltre non
richiede la presenza di più CPU o addirittura di più core. È possibile ottenerlo in un ambiente single-core sfruttando
commutazione di contesto.

Un termine correlato alla concorrenza è il multithreading. Si tratta di una caratteristica dei programmi che consente loro di eseguire più compiti contemporaneamente. Non tutti i programmi utilizzano questo approccio, ma quelli che lo fanno possono essere definiti multithread.

Siamo quasi pronti, manca solo una definizione. Asincronia significa che un programma esegue operazioni non bloccanti.
Inizia un compito e poi continua a fare altre cose in attesa della risposta. Quando riceve la risposta, può reagire.

Tutto questo jazz

Per impostazione predefinita, ogni Applicazione Java viene eseguito in un processo. In questo processo, c'è un thread relativo al file main() metodo di
un'applicazione. Tuttavia, come accennato, è possibile sfruttare i meccanismi di più thread all'interno di un'unica applicazione.
programma.

Eseguibile

Il filo è un Java in cui avviene la magia. Questa è la rappresentazione ad oggetti del thread di cui sopra. A
creare un proprio thread, è possibile estendere il metodo Il filo classe. Tuttavia, non è un approccio consigliato. Fili deve essere usato come meccanismo per eseguire il task. I task sono pezzi di codice che vogliamo eseguire in modalità concorrente. Possiamo definirli utilizzando l'opzione Eseguibile interfaccia.

Ma basta con la teoria, mettiamo il nostro codice al posto della bocca.

Problema

Supponiamo di avere una coppia di matrici di numeri. Per ogni matrice, vogliamo conoscere la somma dei numeri in una matrice. Facciamo
Si supponga che ci siano molti array di questo tipo e che ognuno di essi sia relativamente grande. In queste condizioni, vogliamo sfruttare la concorrenza e sommare ogni array come compito separato.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Runnable task1 = () -> {
    int sum = Arrays.stream(a1).sum();
    System.out.println("1. La somma è: " + sum);
};

Runnable task2 = () -> {
    int sum = Arrays.stream(a2).sum();
    System.out.println("2. La somma è: " + sum);
};

Runnable task3 = () -> {
    int sum = Arrays.stream(a3).sum();
    System.out.println("3. La somma è: " + sum);
};

nuovo Thread(task1).start();
nuovo Thread(task2).start();
nuovo Thread(task3).start();

Come si può vedere dal codice qui sopra Eseguibile è un'interfaccia funzionale. Contiene un singolo metodo astratto eseguire()
senza argomenti. Il Eseguibile dovrebbe essere implementata da qualsiasi classe le cui istanze siano destinate ad essere
eseguito da un thread.

Una volta definito un task, è possibile creare un thread per eseguirlo. Questo può essere fatto tramite nuovo thread() costruttore che
prende Eseguibile come argomento.

Il passo finale consiste nel start() un thread appena creato. Nell'API ci sono anche eseguire() metodi in Eseguibile e in
Il filo. Tuttavia, questo non è un modo per sfruttare la concorrenza in Java. Una chiamata diretta a ciascuno di questi metodi risulta in
eseguire l'attività nello stesso thread in cui si trova il main() viene eseguito il metodo.

Pool di thread ed esecutori

Quando ci sono molti task, creare un thread separato per ciascuno di essi non è una buona idea. Creare un thread Il filo è un
È un'operazione pesante ed è molto meglio riutilizzare i thread esistenti piuttosto che crearne di nuovi.

Quando un programma crea molti thread di breve durata, è meglio utilizzare un pool di thread. Il pool di thread contiene un certo numero di
filoni pronti per essere eseguiti ma attualmente non attivi. Dare un Eseguibile al pool fa sì che uno dei thread richiami il metodo
eseguire() metodo di data Eseguibile. Dopo aver completato un'attività, il thread esiste ancora e si trova in uno stato di inattività.

Ok, avete capito: preferite i pool di thread alla creazione manuale. Ma come si possono utilizzare i pool di thread? Il Esecutori
ha una serie di metodi statici di fabbrica per la costruzione di pool di thread. Ad esempio nuovoCachedThredPool() crea
un pool in cui vengono creati nuovi thread in base alle necessità e i thread inattivi vengono mantenuti per 60 secondi. Al contrario,
newFixedThreadPool() contiene un insieme fisso di thread, in cui i thread inattivi vengono mantenuti indefinitamente.

Vediamo come potrebbe funzionare nel nostro esempio. Ora non è necessario creare manualmente i thread. Dobbiamo invece creare
Servizio Esecutore che fornisce un pool di thread. Poi si possono assegnare i compiti. L'ultimo passo è chiudere il thread
per evitare perdite di memoria. Il resto del codice precedente rimane invariato.

ExecutorService executor = Executors.newCachedThreadPool();

executor.submit(task1);
executor.submit(task2);
executor.submit(task3);

executor.shutdown();

Callable

Eseguibile sembra un modo intelligente per creare task concorrenti, ma ha un grosso difetto. Non può restituire alcun
valore. Inoltre, non possiamo stabilire se un'attività è terminata o meno. Non sappiamo nemmeno se è stato completato.
normalmente o eccezionalmente. La soluzione a questi mali è Callable.

Callable è simile a Eseguibile in un certo senso avvolge anche i task asincroni. La differenza principale è che è in grado di
restituiscono un valore. Il valore di ritorno può essere di qualsiasi tipo (non primitivo), come il parametro Callable è un tipo parametrizzato.
Callable è un'interfaccia funzionale che ha chiamare() che può lanciare un metodo Eccezione.

Vediamo ora come fare leva su Callable nel nostro problema dell'array.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Callable task1 = () -> Arrays.stream(a1).sum();
Callable task2 = () -> Arrays.stream(a2).sum();
Callable task3 = () -> Arrays.stream(a3).sum();

ExecutorService executor = Executors.newCachedThreadPool();
Future future1 = executor.submit(task1);
Future future2 = executor.submit(task2);
Future future3 = executor.submit(task3);

System.out.println("1. La somma è: " + future1.get());
System.out.println("2. La somma è: " + future2.get());
System.out.println("3. La somma è: " + future3.get());

executor.shutdown();

Ok, possiamo vedere come Callable viene creato e poi inviato a Servizio Esecutore. Ma cosa diavolo è Futuro?
Futuro funge da ponte tra i thread. La somma di ogni array viene prodotta in un thread separato e abbiamo bisogno di un modo per
riportare i risultati a main().

Per recuperare il risultato da Futuro dobbiamo chiamare get() metodo. In questo caso possono accadere due cose. In primo luogo, il metodo
risultato del calcolo eseguito da Callable è disponibile. Allora lo otteniamo immediatamente. In secondo luogo, il risultato non è
pronto. In questo caso get() si bloccherà finché il risultato non sarà disponibile.

Futuro Computabile

Il problema con Futuro è che funziona secondo il "paradigma push". Quando si utilizza Futuro devi essere come un capo che
chiede costantemente: "Il tuo compito è finito? È pronto?" finché non fornisce un risultato. Agire sotto costante pressione è
costoso. Un approccio decisamente migliore sarebbe quello di ordinare Futuro cosa fare quando è pronto per il suo compito. Purtroppo,
Futuro non può farlo, ma Futuro Computabile può.

Futuro Computabile funziona secondo il "paradigma pull". Possiamo dirgli cosa fare con il risultato quando ha completato i suoi compiti. Esso
è un esempio di approccio asincrono.

Futuro Computabile funziona perfettamente con Eseguibile ma non con Callable. Invece, è possibile fornire un compito a
Futuro Computabile sotto forma di Fornitore.

Vediamo come quanto detto sopra si collega al nostro problema.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

CompletableFuture.supplyAsync(() -> Arrays.stream(a1).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a2).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a3).sum())
                .thenAccept(System.out::println);

La prima cosa che colpisce è quanto sia più corta questa soluzione. Inoltre, ha un aspetto pulito e ordinato.

Compito di CompletabileFuturo può essere fornito da supplyAsync() che prende il metodo Fornitore o da runAsync() che
prende Eseguibile. Un callback - un pezzo di codice che deve essere eseguito al completamento dell'attività - è definito da thenAccept()
metodo.

Conclusioni

Java offre molti approcci diversi alla concorrenza. In questo articolo abbiamo appena accennato all'argomento.

Tuttavia, abbiamo trattato le basi di Il filo, Eseguibile, Callable, e CallableFuture che pone una buona questione
per approfondire l'argomento.

Articoli correlati

Sviluppo di software

9 errori da evitare durante la programmazione in Java

Quali sono gli errori da evitare durante la programmazione in Java? Nel pezzo che segue rispondiamo a questa domanda.

The Codest
Rafal Sawicki Sviluppatore Java
Soluzioni per aziende e scaleup

Il modo giusto per trovare i migliori sviluppatori Java

Trovare lo sviluppatore Java perfetto può essere un compito arduo. Poiché la domanda di mercato per questi professionisti cresce a un ritmo sorprendente, le fonti disponibili per la ricerca di talenti possono talvolta sembrare...

The Codest
Grzegorz Rozmus Capo unità Java
Soluzioni per aziende e scaleup

10 aziende di Dubai da tenere d'occhio nel 2020

Dubai è il cuore degli Emirati Arabi Uniti con il suo mercato sempre più prospero di aziende globali e promettenti startup. Molte possono vantare un successo internazionale e prodotti degni di nota....

Tonno Pinar
Soluzioni per aziende e scaleup

In che modo Java può supportare la vostra attività?

Prima di iniziare, vorrei ricordarvi una cosa importante. Java non è solo un linguaggio di programmazione.

Bartlomiej Kuczynski
Soluzioni per aziende e scaleup

Un giorno nella vita di un programmatore dell'The Codest

Si potrebbe pensare che gli orari di lavoro dei programmatori non differiscano tra loro. Ma in realtà non è così! Ogni startup, software house o azienda ha i propri...

The Codest
Pawel Rybczynski Software Engineer

Iscrivetevi alla nostra knowledge base e rimanete aggiornati sulle competenze del settore IT.

    Chi siamo

    The Codest - Società internazionale di sviluppo software con centri tecnologici in Polonia.

    Regno Unito - Sede centrale

    • Ufficio 303B, 182-184 High Street North E6 2JA
      Londra, Inghilterra

    Polonia - Poli tecnologici locali

    • Parco uffici Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Ambasciata del cervello, Konstruktorska
      11, 02-673 Varsavia, Polonia

      The Codest

    • Casa
    • Chi siamo
    • Servizi
    • Case Studies
    • Sapere come
    • Carriera
    • Dizionario

      Servizi

    • Consulenza
    • Sviluppo di software
    • Sviluppo backend
    • Sviluppo Frontend
    • Staff Augmentation
    • Sviluppatori backend
    • Ingegneri del cloud
    • Ingegneri dei dati
    • Altro
    • Ingegneri QA

      Risorse

    • Fatti e miti sulla collaborazione con un partner esterno per lo sviluppo di software
    • Dagli Stati Uniti all'Europa: Perché le startup americane decidono di trasferirsi in Europa
    • Confronto tra gli hub di sviluppo Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filippine), Eurasia (Turchia)
    • Quali sono le principali sfide di CTO e CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condizioni di utilizzo del sito web

    Copyright © 2025 di The Codest. Tutti i diritti riservati.

    it_ITItalian
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek it_ITItalian