Multithreading in Java

De Java Virtual Machine ondersteunt parallel computergebruik . Alle berekeningen kunnen worden uitgevoerd in de context van een of meer threads. We kunnen eenvoudig toegang tot dezelfde bron of hetzelfde object instellen voor meerdere threads, en ook een thread instellen om een ​​enkel codeblok uit te voeren.

Elke ontwikkelaar moet werk synchroniseren met threads tijdens lees- en schrijfbewerkingen voor resources waaraan meerdere threads zijn toegewezen.

Het is belangrijk dat u op het moment dat u de bron opent, up-to-date gegevens heeft, zodat een andere thread deze kan wijzigen en u de meest recente informatie krijgt. Zelfs als we het voorbeeld van een bankrekening nemen, totdat het geld erop staat, kun je het niet gebruiken, dus het is belangrijk om altijd up-to-date gegevens te hebben. Java heeft speciale klassen voor het synchroniseren en beheren van threads.

Draad objecten

Het begint allemaal met de hoofdthread, dat wil zeggen dat uw programma in ieder geval al één lopende thread heeft. De hoofdthread kan andere threads maken met Callable of Runnable . De creatie verschilt alleen in het retourresultaat, Runnable retourneert geen resultaat en kan geen aangevinkte uitzondering genereren. Daarom krijg je een goede kans om efficiënt met bestanden te werken, maar dit is erg gevaarlijk en je moet voorzichtig zijn.

Het is ook mogelijk om de uitvoering van threads te plannen op een afzonderlijke CPU-kern. Het systeem kan gemakkelijk schakelen tussen threads en een specifieke thread uitvoeren met de juiste instellingen: dat wil zeggen, de thread die de gegevens leest, wordt eerst uitgevoerd, zodra we gegevens hebben, geven we deze door aan de thread die verantwoordelijk is voor validatie, daarna geven we het door aan de thread om wat bedrijfslogica uit te voeren en een nieuwe thread schrijft ze terug. In zo'n situatie zijn 4 threads achtereenvolgens gegevens aan het verwerken en zal alles sneller werken dan één thread. Elke dergelijke stream wordt geconverteerd naar een native OS-stream, maar hoe deze wordt geconverteerd, hangt af van de JVM-implementatie.

De klasse Thread wordt gebruikt om threads te maken en ermee te werken. Het heeft standaard besturingsmechanismen, maar ook abstracte, zoals klassen en verzamelingen van java.util.concurrent .

Draadsynchronisatie in Java

Communicatie vindt plaats door toegang tot objecten te delen. Dit is zeer effectief, maar tegelijkertijd is het heel gemakkelijk om een ​​fout te maken tijdens het werken. Fouten zijn er in twee gevallen: threadinterferentie - wanneer een andere thread uw thread verstoort, en geheugenconsistentiefouten - geheugenconsistentie. Om deze fouten op te lossen en te voorkomen, hebben we verschillende synchronisatiemethodes.

Threadsynchronisatie in Java wordt afgehandeld door monitors, dit is een mechanisme op hoog niveau waarmee slechts één thread een codeblok tegelijk kan uitvoeren dat door dezelfde monitor wordt beschermd. Het gedrag van monitoren wordt beschouwd in termen van sloten; één monitor - één slot.

Synchronisatie kent een aantal belangrijke punten waar je op moet letten. Het eerste punt is wederzijdse uitsluiting - slechts één thread kan eigenaar zijn van de monitor, dus synchronisatie op de monitor houdt in dat zodra een thread een gesynchroniseerd blok betreedt dat wordt beschermd door de monitor, geen andere thread het blok kan betreden dat wordt beschermd door de monitor. eerste thread verlaat het gesynchroniseerde blok. Dat wil zeggen, meerdere threads kunnen niet tegelijkertijd toegang krijgen tot hetzelfde gesynchroniseerde blok.

Maar synchronisatie is niet alleen wederzijdse uitsluiting. Synchronisatie zorgt ervoor dat gegevens die vóór of binnen een gesynchroniseerd blok naar het geheugen zijn geschreven, zichtbaar worden voor andere threads die op dezelfde monitor zijn gesynchroniseerd. Nadat we het blok hebben verlaten, laten we de monitor los en kan een andere thread deze pakken en beginnen met het uitvoeren van dit codeblok.

Wanneer een nieuwe thread de monitor vastlegt, krijgen we toegang tot en de mogelijkheid om dat codeblok uit te voeren, en op dat moment worden de variabelen uit het hoofdgeheugen geladen. Dan kunnen we alle items zien die zichtbaar zijn gemaakt door de vorige release van de monitor.

Een lees-schrijfbewerking op een veld is een atomaire bewerking als het veld vluchtig wordt verklaard of wordt beschermd door een unieke vergrendeling die is verkregen vóór enige lees-schrijfbewerking. Maar als je toch een fout tegenkomt, dan krijg je een foutmelding over opnieuw bestellen (bestelling wijzigen, opnieuw bestellen). Het manifesteert zich in verkeerd gesynchroniseerde multi-threaded programma's, waarbij één thread de effecten kan waarnemen die door andere threads worden geproduceerd.

Het effect van wederzijdse uitsluiting en synchronisatie van threads, dat wil zeggen, hun correcte werking wordt alleen bereikt door een gesynchroniseerd blok of methode in te voeren die impliciet een vergrendeling verkrijgt, of door expliciet een vergrendeling te verkrijgen. We zullen er hieronder over praten. Beide manieren van werken tasten je geheugen aan en het is belangrijk om het werken met vluchtige variabelen niet te vergeten .

Vluchtige velden op Java

Als een variabele is gemarkeerd als vluchtig , is deze wereldwijd beschikbaar. Dit betekent dat als een thread een vluchtige variabele benadert, deze zijn waarde krijgt voordat de waarde uit de cache wordt gebruikt.

Een schrijfbewerking werkt als een monitorrelease en een leesbewerking werkt als een monitoropname. Toegang wordt uitgevoerd in een relatie van het type "eerder uitgevoerd". Als je het doorhebt, is het enige dat zichtbaar is voor thread A wanneer het een vluchtige variabele benadert, de variabele voor thread B. Dat wil zeggen dat je gegarandeerd je wijzigingen van andere threads niet kwijtraakt.

Vluchtige variabelen zijn atomair, dat wil zeggen dat bij het lezen van een dergelijke variabele hetzelfde effect wordt gebruikt als bij het verkrijgen van een vergrendeling - de gegevens in het geheugen worden ongeldig of onjuist verklaard en de waarde van de vluchtige variabele wordt opnieuw uit het geheugen gelezen. Bij het schrijven wordt het effect op het geheugen gebruikt, evenals bij het ontgrendelen van een slot - een vluchtig veld wordt naar het geheugen geschreven.

Java gelijktijdig

Als u een superefficiënte toepassing met meerdere threads wilt maken, moet u de klassen uit de JavaConcurrent- bibliotheek gebruiken , die zich in het pakket java.util.concurrent bevinden .

De bibliotheek is zeer volumineus en heeft verschillende functies, dus laten we eens kijken naar wat erin zit en het opdelen in enkele modules:

Java gelijktijdig

Concurrent Collections is een verzameling collecties voor het werken in een omgeving met meerdere threads. In plaats van de basiswrapper Collections.synchronizedList met het blokkeren van toegang tot de gehele collectie, worden sloten gebruikt op gegevenssegmenten of worden wachtvrije algoritmen gebruikt om gegevens parallel te lezen.

Wachtrijen - niet-blokkerende en blokkerende wachtrijen voor het werken in een omgeving met meerdere threads. Niet-blokkerende wachtrijen richten zich op snelheid en werking zonder threads te blokkeren. Blokkerende wachtrijen zijn geschikt voor werk wanneer u de Producer- of Consumer- threads moet "vertragen". In een situatie waarin bijvoorbeeld niet aan een aantal voorwaarden wordt voldaan, de wachtrij leeg of vol is, of er geen vrije Consument 'a.

Synchronizers zijn hulpprogramma's voor het synchroniseren van threads. Ze zijn een krachtig wapen in "parallel" computergebruik.

Executors is een raamwerk voor het handiger en eenvoudiger maken van threadpools, het is eenvoudig om de planning van asynchrone taken in te stellen met het verkrijgen van resultaten.

Vergrendelingen zijn veel flexibele threadsynchronisatiemechanismen in vergelijking met de basis gesynchroniseerd , wachten , melden , meldenAlles .

Atomics zijn klassen die atomaire bewerkingen op primitieven en referenties kunnen ondersteunen.