Vodič za sinkroniziranu ključnu riječ na Javi

1. Pregled

Ovaj kratki članak uvod će u upotrebu sinkronizirano blok u Javi.

Jednostavno rečeno, u okruženju s više niti, utrka se događa kada dvije ili više niti pokušavaju istodobno ažurirati promjenjive dijeljene podatke. Java nudi mehanizam za izbjegavanje rasnih uvjeta sinkroniziranjem pristupa niti zajedničkim podacima.

Dio logike označen s sinkronizirano postaje sinkronizirani blok, dopuštajući izvršavanje samo jedne niti u bilo kojem trenutku.

2. Zašto sinkronizacija?

Razmotrimo tipično trkačko stanje u kojem izračunavamo zbroj i više niti izvršava izračunati() metoda:

javna klasa BaeldungSynchronizedMethods {private int sum = 0; javna praznina izračunaj () {setSum (getSum () + 1); } // standardni postavljači i dobivači} 

I napišimo jednostavan test:

@Test javna praznina givenMultiThread_whenNonSyncMethod () {ExecutorService service = Executors.newFixedThreadPool (3); Zbir BaeldungSynchronizedMethods = novi BaeldungSynchronizedMethods (); IntStream.range (0, 1000) .forEach (count -> service.submit (sumacija :: izračunaj)); service.awaitTermination (1000, TimeUnit.MILLISECONDS); assertEquals (1000, summation.getSum ()); }

Jednostavno koristimo ExecutorService s bazenom od 3 niti za izvršavanje izračunati() 1000 puta.

Ako bismo ovo izvršavali serijski, očekivani izlaz bio bi 1000, ali naša izvedba s više niti završava gotovo svaki put s nedosljednim stvarnim izlazom, npr .:

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) ...

Ovaj rezultat naravno nije neočekivan.

Jednostavan način za izbjegavanje uvjeta utrke je učiniti operaciju sigurnom za nit korištenjem sinkronizirano ključna riječ.

3. The Sinkronizirano Ključna riječ

The sinkronizirano ključna riječ može se koristiti na različitim razinama:

  • Instance metode
  • Statičke metode
  • Blokovi koda

Kada koristimo a sinkronizirano blok, interno Java koristi monitor također poznat i kao zaključavanje monitora ili unutarnja brava kako bi osigurao sinkronizaciju. Ti su monitori vezani za objekt, tako da svi sinkronizirani blokovi istog objekta mogu imati samo jednu nit koja ih izvršava istodobno.

3.1. Sinkronizirano Instance metode

Jednostavno dodajte sinkronizirano ključna riječ u deklaraciji metode kako bi se metoda sinkronizirala:

javna sinkronizirana praznina synchronisedCalculate () {setSum (getSum () + 1); }

Primijetite da nakon što sinkroniziramo metodu, test prođe, a stvarni izlaz je 1000:

@Test javna praznina givenMultiThread_whenMethodSync () {ExecutorService service = Executors.newFixedThreadPool (3); SynchronizedMethods method = novi SynchronizedMethods (); IntStream.range (0, 1000) .forEach (count -> service.submit (method :: synchronisedCalculate)); service.awaitTermination (1000, TimeUnit.MILLISECONDS); assertEquals (1000, method.getSum ()); }

Instance metode su sinkronizirano preko instance klase koja posjeduje metodu. Što znači da samo jedna nit po instanci klase može izvršiti ovu metodu.

3.2. Sinkronizirano Static Metode

Statičke metode su sinkronizirano baš poput metoda instance:

 javna statička sinkronizirana praznina syncStaticCalculate () {staticSum = staticSum + 1; }

Ove metode su sinkronizirano na Razred objekt povezan s klasom i budući da je samo jedan Razred objekt postoji po JVM-u po klasi, samo jedna nit može se izvršiti unutar a statički sinkronizirano metodu po klasi, bez obzira na broj slučajeva koje ima.

Isprobajmo:

@Test javna praznina givenMultiThread_whenStaticSyncMethod () {ExecutorService service = Executors.newCachedThreadPool (); IntStream.range (0, 1000) .forEach (count -> service.submit (BaeldungSynchronizedMethods :: syncStaticCalculate)); service.awaitTermination (100, TimeUnit.MILLISECONDS); assertEquals (1000, BaeldungSynchronizedMethods.staticSum); }

3.3. Sinkronizirano Blokovi unutar metoda

Ponekad ne želimo sinkronizirati cijelu metodu već samo neke upute u njoj. To se može postići prijavljivanje sinkronizirano s blokom:

javna praznina performSynchronisedTask () {sinkronizirano (ovo) {setCount (getCount () + 1); }}

Isprobajmo promjenu:

@Test javna praznina givenMultiThread_whenBlockSync () {ExecutorService service = Executors.newFixedThreadPool (3); BaeldungSynchronizedBlocks synchronizedBlocks = novi BaeldungSynchronizedBlocks (); IntStream.range (0, 1000) .forEach (count -> service.submit (synchronizedBlocks :: performSynchronisedTask)); service.awaitTermination (100, TimeUnit.MILLISECONDS); assertEquals (1000, synchronizedBlocks.getCount ()); }

Primijetite da smo proslijedili parametar ovaj prema sinkronizirano blok. Ovo je objekt monitora, kôd unutar bloka sinkronizira se na objektu monitora. Jednostavno rečeno, samo jedna nit po objektu monitora može se izvršiti unutar tog bloka koda.

U slučaju da je metoda statički, dodali bismo ime klase umjesto reference objekta. A klasa bi bila monitor za sinkronizaciju bloka:

javna statička praznina performStaticSyncTask () {sinkronizirano (SynchronisedBlocks.class) {setStaticCount (getStaticCount () + 1); }}

Isprobajmo blok unutar statički metoda:

@Test javna praznina givenMultiThread_whenStaticSyncBlock () {ExecutorService service = Executors.newCachedThreadPool (); IntStream.range (0, 1000) .forEach (count -> service.submit (BaeldungSynchronizedBlocks :: performStaticSyncTask)); service.awaitTermination (100, TimeUnit.MILLISECONDS); assertEquals (1000, BaeldungSynchronizedBlocks.getStaticCount ()); }

3.4. Ponovno ulazak

Brava iza sinkronizirano metode i blokade vraća se. Odnosno, trenutna nit može steći isto sinkronizirano zaključajte iznova i iznova dok je držite:

Zaključavanje objekta = novi objekt (); sinkronizirano (zaključavanje) {System.out.println ("Prvi put kad ga nabavljam"); sinkronizirano (zaključavanje) {System.out.println ("Ponovni ulazak"); sinkronizirano (zaključavanje) {System.out.println ("I opet"); }}}

Kao što je prikazano gore, dok smo u sinkronizirano blok, možemo više puta dobiti istu bravu monitora.

4. Zaključak

U ovom kratkom članku vidjeli smo različite načine korištenja sinkronizirano ključna riječ za postizanje sinkronizacije niti.

Također smo istražili kako stanje utrke može utjecati na našu aplikaciju i kako nam sinkronizacija pomaže da to izbjegnemo. Za više informacija o sigurnosti niti pomoću bravica u Javi pogledajte našu java.util.concurrent.Locks članak.

Kompletni kôd ovog vodiča dostupan je na GitHub-u.