Korištenje mutex objekta u Javi

1. Pregled

U ovom uputstvu ćemo vidjeti različiti načini implementacije mutexa u Javi.

2. Muteks

U višenitnoj aplikaciji dvije ili više niti možda će trebati istovremeno pristupiti zajedničkom resursu, što rezultira neočekivanim ponašanjem. Primjeri takvih zajedničkih resursa su strukture podataka, uređaji za ulaz-izlaz, datoteke i mrežne veze.

Ovaj scenarij nazivamo a stanje rase. A dio programa koji pristupa zajedničkom resursu poznat je kao kritični odjeljak. Dakle, da bismo izbjegli uvjete utrke, moramo sinkronizirati pristup kritičnom dijelu.

Mutex (ili međusobno isključivanje) najjednostavnija je vrsta sinkronizator - to osigurava da samo jedna nit može istodobno izvršiti kritični odjeljak računalnog programa.

Da bi pristupila kritičnom odjeljku, nit stječe mutex, zatim pristupa kritičnom odjeljku i na kraju oslobađa mutex. U međuvremenu, sve ostale niti blokiraju se dok se mutex ne oslobodi. Čim nit izađe iz kritičnog odjeljka, druga nit može ući u kritični odjeljak.

3. Zašto baš Mutex?

Prvo uzmimo primjer a SequenceGeneraror klase, koja generira sljedeću sekvencu povećavanjem Trenutna vrijednost svaki put po jedan:

javna klasa SequenceGenerator {private int currentValue = 0; javni int getNextSequence () {currentValue = currentValue + 1; vratiti currentValue; }}

Sada, kreirajmo testni slučaj da bismo vidjeli kako se ova metoda ponaša kada više niti istovremeno pokušava pristupiti njoj:

@Test javna praznina givenUnsafeSequenceGenerator_whenRaceCondition_thenUnexpectedBehavior () baca iznimku {int count = 1000; Postavite uniqueSequences = getUniqueSequences (novi SequenceGenerator (), count); Assert.assertEquals (count, uniqueSequences.size ()); } private Set getUniqueSequences (Generator SequenceGenerator, int count) baca iznimku {ExecutorService izvršitelj = Izvršitelji.newFixedThreadPool (3); Postavi uniqueSequences = novi LinkedHashSet (); Popis futures = novi ArrayList (); for (int i = 0; i <count; i ++) {futures.add (izvršilac.submit (generator :: getNextSequence)); } za (Budućnost: futures) {uniqueSequences.add (future.get ()); } izvršitelj.awaitTermination (1, TimeUnit.SECONDS); egzekutor.šutdown (); povratak uniqueSequences; }

Jednom kada izvršimo ovaj testni slučaj, možemo vidjeti da on većinom ne uspije iz razloga sličnog:

java.lang.AssertionError: očekuje se: ali bilo je: na org.junit.Assert.fail (Assert.java:88) na org.junit.Assert.failNotEquals (Assert.java:834) na org.junit.Assert.assertEquals ( Assert.java:645)

The uniqueSequences treba imati veličinu jednaku broju puta kada smo izvršili getNextSequence metoda u našem testnom slučaju. Međutim, to nije slučaj zbog uvjeta utrke. Očito je da ne želimo takvo ponašanje.

Dakle, da bismo izbjegli takve uvjete utrke, moramo pobrinite se da samo jedna nit može izvršiti getNextSequence metoda odjednom. U takvim scenarijima možemo koristiti mutex za sinkronizaciju niti.

Postoje različiti načini, možemo primijeniti mutex u Javi. Dakle, sljedeće ćemo vidjeti različite načine za implementaciju mutexa za naš Generator sekvence razred.

4. Korištenje sinkronizirano Ključna riječ

Prvo ćemo razgovarati o sinkronizirano ključna riječ, što je najjednostavniji način implementacije mutexa u Javi.

Svaki objekt u Javi povezan je s vlastitom bravom. Thesinkronizirano metoda ithe sinkronizirano blok koristite ovu unutarnju bravu za ograničavanje pristupa kritičnog odjeljka samo na jednu nit istovremeno.

Stoga, kada nit pozove a sinkronizirano metodu ili unosi a sinkronizirano blok, automatski dobiva bravu. Zaključavanje se otpušta kada se metoda ili blok dovrše ili se iz njih izbaci iznimka.

Promijenimo se getNextSequence da imate mutex, jednostavno dodavanjem sinkronizirano ključna riječ:

javna klasa SequenceGeneratorUsingSynchronizedMethod proširuje SequenceGenerator {@Override public synchronized int getNextSequence () {return super.getNextSequence (); }}

The sinkronizirano blok je sličan sinkronizirano metodom, s više kontrole nad kritičnim presjekom i objektom koji možemo koristiti za zaključavanje.

Pa, hajde sada da vidimo kako možemo koristiti sinkronizirano blok za sinkronizaciju na prilagođenom mutex objektu:

javna klasa SequenceGeneratorUsingSynchronizedBlock proširuje SequenceGenerator {privatni objekt mutex = novi objekt (); @Override public int getNextSequence () {sinkronizirano (mutex) {return super.getNextSequence (); }}}

5. Korištenje ReentrantLock

The ReentrantLock razred uveden je u Javi 1.5. Pruža veću fleksibilnost i kontrolu od sinkronizirano pristup ključnim riječima.

Pogledajmo kako možemo koristiti ReentrantLock kako bi se postiglo uzajamno isključivanje:

javna klasa SequenceGeneratorUsingReentrantLock proširuje SequenceGenerator {private ReentrantLock mutex = new ReentrantLock (); @Override public int getNextSequence () {try {mutex.lock (); vrati super.getNextSequence (); } napokon {mutex.unlock (); }}}

6. Korištenje Semafor

Kao ReentrantLock, Semafor klasa je uvedena i u Javi 1.5.

Iako u slučaju mutexa samo jedna nit može pristupiti kritičnom odjeljku, Semafor dopušta fiksni broj niti za pristup kritičnom odjeljku. Stoga, također možemo implementirati mutex postavljanjem broja dopuštenih niti u a Semafor prema jednom.

Stvorimo sada novu verziju Generator sekvence koristeći Semafor:

javna klasa SequenceGeneratorUsingSemaphore proširuje SequenceGenerator {private Semaphore mutex = new Semaphore (1); @Override public int getNextSequence () {try {mutex.acquire (); vrati super.getNextSequence (); } catch (InterruptedException e) {// kod za rukovanje iznimkama} konačno {mutex.release (); }}}

7. Korištenje Guave Monitor Razred

Do sada smo vidjeli opcije za implementaciju mutexa pomoću značajki koje nudi Java.

Međutim Monitor klasa Googleove biblioteke Guava bolja je alternativa ReentrantLock razred. Prema svojoj dokumentaciji, kodiranje pomoću Monitor je čitljiviji i manje podložan pogreškama od koda koji koristi ReentrantLock.

Prvo ćemo dodati ovisnost Mavena za Guavu:

 com.google.guava guava 28,0-jre 

Sada ćemo napisati još jedan podrazred od Generator sekvence koristiti Monitor razred:

javna klasa SequenceGeneratorUsingMonitor proširuje SequenceGenerator {private Monitor mutex = new Monitor (); @Override public int getNextSequence () {mutex.enter (); probajte {return super.getNextSequence (); } napokon {mutex.leave (); }}}

8. Zaključak

U ovom uputstvu proučili smo pojam mutexa. Također, vidjeli smo različite načine kako ga implementirati u Javi.

Kao i uvijek, cjeloviti izvorni kod primjera koda korištenih u ovom vodiču dostupan je na GitHub-u.