window.pipedriveLeadboosterConfig = { basis: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versie: 2, } ;(functie () { var w = venster als (w.LeadBooster) { console.warn('LeadBooster bestaat al') } anders { w.LeadBooster = { q: [], on: functie (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: functie (n) { this.q.push({ t: 't', n: n }) }, } } })() Concurrency in Java Deel 1 - Inleiding - The Codest
The Codest
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Industrie
    • Fintech & Bankieren
    • E-commerce
    • Adtech
    • Gezondheidstechnologie
    • Productie
    • Logistiek
    • Automotive
    • IOT
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
Pijl terug KEREN TERUG
2022-06-15
Software Ontwikkeling

Concurrency in Java Deel 1 - Inleiding

The Codest

Rafal Sawicki

Java Ontwikkelaar

Lees het eerste deel van onze blogserie over concurrency in Java. In het volgende artikel gaan we dieper in op de verschillen tussen threads en processen, thread pools, executors en nog veel meer!

In het algemeen is de conventionele programmeeraanpak sequentieel. Alles in een programma gebeurt stap voor stap.
Maar in feite draait de hele wereld parallel - het is de mogelijkheid om meer dan één taak tegelijkertijd uit te voeren.

Draad vs. proces

Om geavanceerde onderwerpen te bespreken zoals gelijktijdigheid in Java of multithreading, moeten we het eens worden over enkele gemeenschappelijke definities om er zeker van te zijn dat we op dezelfde pagina zitten.

Laten we beginnen met de basis. In de niet-sequentiële wereld hebben we twee soorten concurrency representanten: processen en
threads. Een proces is een instantie van het programma dat draait. Normaal gesproken is het geïsoleerd van andere processen.
Het besturingssysteem is verantwoordelijk voor het toewijzen van bronnen aan elk proces. Bovendien fungeert het als een geleider die
plant en controleert ze.

Thread is een soort proces, maar op een lager niveau, daarom wordt het ook wel light thread genoemd. Meerdere threads kunnen in één
proces. Hier fungeert het programma als een scheduler en een controller voor threads. Op deze manier lijkt het alsof individuele programma's
meerdere taken tegelijkertijd.

Het fundamentele verschil tussen threads en processen is het isolatieniveau. Het proces heeft zijn eigen set van
bronnen, terwijl de thread gegevens deelt met andere threads. Het lijkt misschien een foutgevoelige aanpak en dat is het ook. Voor
Laten we ons daar nu niet op richten, want dat valt buiten het bestek van dit artikel.

Processen, threads - ok... Maar wat is concurrency precies? Concurrency betekent dat je meerdere taken tegelijk kunt uitvoeren.
tijd. Het betekent niet dat die taken gelijktijdig moeten worden uitgevoerd - dat is wat parallellisme is. Concurrenc in Javay ook niet
meerdere CPU's of zelfs meerdere cores nodig. Het kan worden bereikt in een single-core omgeving door gebruik te maken van
contextschakelen.

Een term die gerelateerd is aan concurrency is multithreading. Dit is een eigenschap van programma's waarmee ze meerdere taken tegelijk kunnen uitvoeren. Niet elk programma gebruikt deze aanpak, maar de programma's die dat wel doen, kunnen multithreaded worden genoemd.

We zijn bijna klaar, nog één definitie. Asynchronie betekent dat een programma niet-blokkerende bewerkingen uitvoert.
Het initieert een taak en gaat dan verder met andere dingen terwijl het wacht op het antwoord. Wanneer het de reactie krijgt, kan het erop reageren.

Al die jazz

Standaard wordt elke Java-toepassing draait in één proces. In dat proces is er één thread gerelateerd aan de hoofd() methode van
een applicatie. Zoals gezegd is het echter mogelijk om gebruik te maken van de mechanismen van meerdere threads binnen één
programma.

Runnable

Draad is een Java klasse waarin de magie gebeurt. Dit is de objectrepresentatie van de eerder genoemde thread. Naar
je eigen thread maken, kun je de Draad klasse. Het is echter geen aanbevolen aanpak. Draden moet worden gebruikt als een mechanisme om de taak uit te voeren. Taken zijn stukjes code die we in een gelijktijdige modus willen uitvoeren. We kunnen ze definiëren met de Runnable interface.

Maar genoeg theorie, laten we de daad bij het woord voegen.

Probleem

Stel dat we een aantal matrices met getallen hebben. Voor elke matrix willen we de som van de getallen in een matrix weten. Laten we
doen alsof er veel van zulke arrays zijn en elk ervan relatief groot is. In dergelijke omstandigheden willen we gebruik maken van concurrency en elke array als een aparte taak optellen.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {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. De som is: " + som);
};

Runnable task2 = () -> {
    int sum = Arrays.stream(a2).sum();
    System.out.println("2. De som is: " + som);
};

Runnable task3 = () -> {
    int sum = Arrays.stream(a3).sum();
    System.out.println("3. De som is: " + som);
};

new Thread(task1).start();
nieuwe Thread(task2).start();
nieuwe Thread(task3).start();

Zoals je kunt zien in de bovenstaande code Runnable is een functionele interface. Het bevat één abstracte methode uitvoeren()
zonder argumenten. De Runnable interface moet geïmplementeerd worden door elke klasse waarvan de instanties bedoeld zijn om
uitgevoerd door een thread.

Zodra je een taak hebt gedefinieerd, kun je een thread maken om de taak uit te voeren. Dit kan via nieuwe draad() constructor die
neemt Runnable als argument.

De laatste stap is start() een nieuw aangemaakte thread. In de API zijn er ook uitvoeren() methoden in Runnable en in
Draad. Dat is echter geen manier om gebruik te maken van concurrency in Java. Een directe aanroep naar elk van deze methoden resulteert in
de taak uitvoeren in dezelfde thread als de hoofd() methode wordt uitgevoerd.

Draadpools en uitvoerders

Als er veel taken zijn, is het geen goed idee om voor elke taak een aparte thread aan te maken. Een Draad is een
en het is veel beter om bestaande threads te hergebruiken dan om nieuwe te maken.

Als een programma veel kortstondige threads aanmaakt, is het beter om een threadpool te gebruiken. De threadpool bevat een aantal
threads die klaar zijn om te worden uitgevoerd, maar momenteel niet actief zijn. Een Runnable naar de pool zorgt ervoor dat een van de threads de
uitvoeren() methode van gegeven Runnable. Na het voltooien van een taak bestaat de thread nog steeds en bevindt zich in een inactieve toestand.

Ok, je snapt het - liever een threadpool dan handmatig aanmaken. Maar hoe kun je threadpools gebruiken? De Executeurs
klasse heeft een aantal statische fabrieksmethoden voor het bouwen van threadpools. Bijvoorbeeld nieuweCachedThredPool() creëert
een pool waarin nieuwe threads worden aangemaakt als dat nodig is en niet-actieve threads 60 seconden worden vastgehouden. In tegenstelling,
newFixedThreadPool() bevat een vaste set threads, waarin idle threads voor onbepaalde tijd worden bewaard.

Laten we eens kijken hoe het in ons voorbeeld zou kunnen werken. Nu hoeven we threads niet handmatig aan te maken. In plaats daarvan moeten we
UitvoerderService die zorgt voor een pool van threads. Vervolgens kunnen we er taken aan toewijzen. De laatste stap is het sluiten van de thread
pool om geheugenlekken te voorkomen. De rest van de vorige code blijft hetzelfde.

ExecutorService executor = Executors.newCachedThreadPool();

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

executor.shutdown();

Opvraagbaar

Runnable lijkt een handige manier om gelijktijdige taken te maken, maar het heeft één grote tekortkoming. Het kan geen
waarde. Bovendien kunnen we niet bepalen of een taak voltooid is of niet. We weten ook niet of de taak is voltooid
normaal of uitzonderlijk. De oplossing voor deze kwalen is Opvraagbaar.

Opvraagbaar is vergelijkbaar met Runnable op een bepaalde manier ook asynchrone taken. Het belangrijkste verschil is dat het in staat is om
een waarde teruggeven. De retourwaarde kan van elk (niet-primitief) type zijn als de Opvraagbaar interface is een geparametriseerd type.
Opvraagbaar is een functionele interface die oproep() methode die een Uitzondering.

Laten we nu eens kijken hoe we gebruik kunnen maken van Opvraagbaar in ons matrixprobleem.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {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).som();
Callable task3 = () -> Arrays.stream(a3).som();

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

System.out.println("1. De som is: " + future1.get());
System.out.println("2. De som is: " + future2.get());
System.out.println("3. De som is: " + future3.get());

executor.shutdown();

Oké, we kunnen zien hoe Opvraagbaar wordt gemaakt en vervolgens ingediend bij UitvoerderService. Maar wat is Toekomst?
Toekomst fungeert als een brug tussen threads. De som van elke array wordt geproduceerd in een aparte thread en we hebben een manier nodig om
die resultaten terugsturen naar hoofd().

Om het resultaat op te halen uit Toekomst moeten we bellen krijgen() methode. Hier kunnen twee dingen gebeuren. Ten eerste wordt de
resultaat van de berekening uitgevoerd door Opvraagbaar beschikbaar is. Dan krijgen we het onmiddellijk. Ten tweede is het resultaat niet
nog niet klaar. In dat geval krijgen() methode zal blokkeren totdat het resultaat beschikbaar is.

ComputableFuture

Het probleem met Toekomst is dat het werkt in het 'push-paradigma'. Bij gebruik van Toekomst je moet zijn als een baas die
vraagt voortdurend: "Is je taak klaar? Is het klaar?" totdat het resultaat oplevert. Handelen onder constante druk is
duur. Een veel betere aanpak zou zijn om Toekomst wat te doen als het klaar is met zijn taak. Helaas,
Toekomst kan dat niet doen, maar ComputableFuture kan.

ComputableFuture werkt in het 'pull-paradigma'. We kunnen het vertellen wat te doen met het resultaat wanneer het zijn taken heeft voltooid. Het
is een voorbeeld van een asynchrone aanpak.

ComputableFuture werkt perfect met Runnable maar niet met Opvraagbaar. In plaats daarvan is het mogelijk om een taak te leveren aan
ComputableFuture in de vorm van Leverancier.

Laten we eens kijken hoe het bovenstaande verband houdt met ons probleem.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {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);

Het eerste wat opvalt is hoeveel korter deze oplossing is. Daarnaast ziet het er ook nog eens netjes en opgeruimd uit.

Taak naar InvulbaarToekomst kan worden geleverd door supplyAsync() methode die Leverancier of door runAsync() dat
neemt Runnable. Een callback - een stukje code dat moet worden uitgevoerd na het voltooien van een taak - wordt gedefinieerd door thenAccept()
methode.

Conclusies

Java biedt veel verschillende benaderingen van gelijktijdigheid. In dit artikel hebben we het onderwerp nauwelijks aangestipt.

Niettemin hebben we de basisbeginselen van Draad, Runnable, Opvraagbaaren CallableFuture wat een goed punt is
voor verder onderzoek.

Verwante artikelen

Software Ontwikkeling

9 fouten die je moet vermijden bij het programmeren in Java

Welke fouten moet je vermijden bij het programmeren in Java? In het volgende stuk beantwoorden we deze vraag.

The Codest
Rafal Sawicki Java Ontwikkelaar
Oplossingen voor ondernemingen en schaalvergroting

De juiste manier om de beste Java-ontwikkelaars te vinden

De perfecte Java-ontwikkelaar vinden kan een ontmoedigende taak zijn. Terwijl de vraag naar dergelijke professionals op de markt razendsnel groeit, lijken de beschikbare bronnen voor het zoeken naar talent soms...

The Codest
Grzegorz Rozmus Java Eenheid Leader
Oplossingen voor ondernemingen en schaalvergroting

10 bedrijven in Dubai die in 2020 in de gaten moeten worden gehouden

Dubai is het hart van de Verenigde Arabische Emiraten met zijn steeds welvarender markt van internationale bedrijven en veelbelovende startups. Velen kunnen bogen op hun internationale succes en opmerkelijke producten ....

Tonijn Pinar
Oplossingen voor ondernemingen en schaalvergroting

Hoe kan Java uw bedrijf ondersteunen?

Voordat we beginnen, wil ik je aan één belangrijk ding herinneren. Java is niet alleen een programmeertaal.

Bartlomiej Kuczynski
Oplossingen voor ondernemingen en schaalvergroting

Een dag uit het leven van een programmeur bij The Codest

Je zou kunnen vermoeden dat de werkschema's van programmeurs niet van elkaar verschillen. Maar dit is eigenlijk niet waar! Elke startup, elk softwarebedrijf en zelfs elk...

The Codest
Pawel Rybczynski Software Engineer

Abonneer je op onze kennisbank en blijf op de hoogte van de expertise uit de IT-sector.

    Over ons

    The Codest - Internationaal softwareontwikkelingsbedrijf met technische hubs in Polen.

    Verenigd Koninkrijk - Hoofdkantoor

    • Kantoor 303B, 182-184 High Street North E6 2JA
      Londen, Engeland

    Polen - Lokale technologieknooppunten

    • Fabryczna kantorenpark, Aleja
      Pokoju 18, 31-564 Krakau
    • Hersenambassade, Konstruktorska
      11, 02-673 Warschau, Polen

      The Codest

    • Home
    • Over ons
    • Diensten
    • Case Studies
    • Weten hoe
    • Carrière
    • Woordenboek

      Diensten

    • Het advies
    • Software Ontwikkeling
    • Backend ontwikkeling
    • Frontend ontwikkeling
    • Staff Augmentation
    • Backend ontwikkelaars
    • Cloud Ingenieurs
    • Gegevensingenieurs
    • Andere
    • QA ingenieurs

      Bronnen

    • Feiten en fabels over samenwerken met een externe partner voor softwareontwikkeling
    • Van de VS naar Europa: Waarom Amerikaanse startups besluiten naar Europa te verhuizen
    • Tech Offshore Ontwikkelingshubs Vergelijking: Tech Offshore Europa (Polen), ASEAN (Filippijnen), Eurazië (Turkije)
    • Wat zijn de grootste uitdagingen voor CTO's en CIO's?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Gebruiksvoorwaarden website

    Copyright © 2025 door The Codest. Alle rechten voorbehouden.

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