Pitanja o intervjuu za Java istovremeno (+ odgovori)

Ovaj je članak dio serije: • Pitanja za intervju za Java Collections

• Pitanja o intervjuu za sustav tipa Java

• Pitanja o intervjuu za Java paralelnost (+ odgovori) (trenutni članak) • Pitanja o intervjuu za strukturu klase i inicijalizaciju Java

• Java 8 pitanja za intervju (+ odgovori)

• Upravljanje memorijom u Java intervjuu Pitanja (+ odgovori)

• Pitanja o intervjuu za Java Generics (+ odgovori)

• Pitanja za intervju s Java Flow Control (+ odgovori)

• Pitanja o intervjuu za iznimke Java (+ odgovori)

• Pitanja za intervju s Java Annotations (+ odgovori)

• Najpopularnija pitanja za proljetni okvirni intervju

1. Uvod

Istodobnost u Javi jedna je od najsloženijih i najnaprednijih tema pokrenutih tijekom tehničkih intervjua. Ovaj članak daje odgovore na neka pitanja iz razgovora na temu s kojom se možete susresti.

Q1. Koja je razlika između procesa i niti?

I procesi i niti su jedinice istodobnosti, ali imaju temeljnu razliku: procesi ne dijele zajedničku memoriju, dok niti dijele.

S gledišta operativnog sustava, proces je neovisan softver koji se pokreće u vlastitom prostoru virtualne memorije. Bilo koji multitasking operativni sustav (što znači gotovo svaki moderni operativni sustav) mora razdvojiti procese u memoriji kako jedan neuspjeli proces ne bi povukao sve ostale procese kodiranjem zajedničke memorije.

Procesi su stoga obično izolirani i surađuju sredstvima međuprocesne komunikacije koju operativni sustav definira kao neku vrstu posrednog API-ja.

Suprotno tome, nit je dio aplikacije koja dijeli zajedničku memoriju s ostalim nitima iste aplikacije. Korištenje zajedničke memorije omogućuje brijanje puno dodatnih troškova, dizajniranje niti za suradnju i razmjenu podataka između njih puno brže.

Q2. Kako možete stvoriti instancu niti i pokrenuti je?

Imate dvije mogućnosti za stvaranje instance niti. Prvo prođite a Izvodljivo instancu svom konstruktoru i pozivu početak(). Izvodljivo je funkcionalno sučelje, pa se može proslijediti kao lambda izraz:

Thread thread1 = new Thread (() -> System.out.println ("Hello World from Runnable!")); thread1.start ();

Navoj također implementira Izvodljivo, pa je drugi način pokretanja niti stvaranje anonimne podklase koja će je nadjačati trčanje() metodu, a zatim nazovite početak():

Thread thread2 = new Thread () {@Preuzmi javni void run () {System.out.println ("Hello World iz podrazreda!"); }}; thread2.start ();

Q3. Opišite različita stanja niti i kada se događaju prijelazi države.

Stanje a Nit može se provjeriti pomoću Thread.getState () metoda. Različita stanja a Nit opisani su u Konac.Država nabrajanje. Oni su:

  • NOVI - novi Nit instancu koja još nije pokrenuta putem Thread.start ()
  • RANJALI - konac koji trči. Naziva se izvodljivim, jer u bilo kojem trenutku može biti pokrenut ili čeka sljedeći kvantum vremena iz planera niti. A NOVI nit ulazi u RANJALI država kad zovete Thread.start () na tome
  • BLOKIRAN - pokrenuta nit postaje blokirana ako treba ući u sinkronizirani odjeljak, ali to ne može učiniti zbog druge niti koja drži monitor ovog odjeljka
  • ČEKANJE - nit ulazi u ovo stanje ako čeka da druga nit izvrši određenu radnju. Na primjer, nit ulazi u ovo stanje nakon poziva Object.wait () metoda na monitoru koji drži ili Thread.join () metoda na drugoj niti
  • VRIJEME_ČEKANJE - isto kao gore, ali nit ulazi u ovo stanje nakon pozivanja vremenskih verzija Thread.sleep (), Object.wait (), Thread.join () i neke druge metode
  • PRESTANAK - nit je dovršio svoje izvršavanje Runnable.run () metoda i ukinut

Q4. Koja je razlika između sučelja za pokretanje i za pozivanje? Kako se koriste?

The Izvodljivo sučelje ima jedan trčanje metoda. Predstavlja jedinicu izračuna koja se mora izvesti u zasebnoj niti. The Izvodljivo sučelje ne dopušta ovoj metodi vraćanje vrijednosti ili bacanje neprovjerenih iznimaka.

The Pozivno sučelje ima jedan poziv metoda i predstavlja zadatak koji ima vrijednost. Zato je poziv metoda vraća vrijednost. Također može donijeti iznimke. Pozivno se općenito koristi u ExecutorService instanci za pokretanje asinkronog zadatka, a zatim poziv povratka Budućnost instancu da dobije svoju vrijednost.

P5. Što je Daemon nit, koji su njezini slučajevi upotrebe? Kako možete stvoriti Daemon nit?

Demon nit je nit koja ne sprečava izlaz JVM-a. Kad se sve ne-demonske niti prekinu, JVM jednostavno napusti sve preostale demonske niti. Daemon niti se obično koriste za izvršavanje nekih pomoćnih ili servisnih zadataka za druge niti, no trebali biste uzeti u obzir da se u bilo kojem trenutku mogu napustiti.

Da biste započeli nit kao demon, trebali biste koristiti setDaemon () metoda prije pozivanja početak():

Daemon teme = novi Thread (() -> System.out.println ("Pozdrav iz demona!")); daemon.setDaemon (true); daemon.start ();

Zanimljivo je da ako ovo pokrenete kao dio glavni() metodom, poruka se možda neće ispisati. To bi se moglo dogoditi ako glavni() nit bi se prekinuo prije nego što bi demon došao do točke ispisa poruke. Općenito ne biste trebali raditi bilo koji I / O u daemon nitima, jer oni niti neće moći izvršiti svoje konačno blokira i zatvori resurse ako se napusti.

P6. Što je zastava prekida navoja? Kako to možete postaviti i provjeriti? Kako se to odnosi na prekinuto izuzeće?

Oznaka prekida ili status prekida interna je Nit zastava koja se postavlja kada se nit prekida. Da biste ga postavili, jednostavno nazovite nit.prekinuti () na objektu niti.

Ako je nit trenutno unutar jedne od metoda koje bacaju InterruptedException (čekati, pridružiti, spavati itd.), tada ova metoda odmah baca InterruptedException. Nit može slobodno obrađivati ​​ovu iznimku prema svojoj logici.

Ako nit nije unutar takve metode i nit.prekinuti () se zove, ne događa se ništa posebno. Odgovornost je niti povremeno provjeravati status prekida pomoću statička nit.prekinuta () ili primjer isInterrupted () metoda. Razlika između ovih metoda je u tome što statička nit.prekinuta () briše zastavicu prekida, dok isInterrupted () ne.

P7. Što su izvršitelj i izvršitelj? Koje su razlike između ovih sučelja?

Izvršitelj i ExecutorService su dva povezana sučelja java.util.concurrent okvir. Izvršitelj je vrlo jednostavno sučelje s jednim izvršiti metoda prihvaćanja Izvodljivo instance za izvršenje. U većini slučajeva to je sučelje o kojem bi trebao ovisiti vaš kôd za izvršavanje zadataka.

ExecutorService proširuje Izvršitelj sučelje s više metoda za rukovanje i provjeru životnog ciklusa istodobne usluge izvršavanja zadataka (prekid zadataka u slučaju gašenja) i metoda za složenije asinkrono rukovanje zadacima, uključujući Budućnosti.

Za više informacija o korištenju Izvršitelj i ExecutorService, pogledajte članak Vodič za Java ExecutorService.

P8. Koje su dostupne implementacije izvršne usluge u standardnoj knjižnici?

The ExecutorService sučelje ima tri standardne implementacije:

  • ThreadPoolExeecuter - za izvršavanje zadataka pomoću skupa niti. Kad je nit završena s izvršavanjem zadatka, vraća se u spremište. Ako su sve niti u spremištu zauzete, tada zadatak mora pričekati svoj red.
  • ScheduledThreadPoolExecutor omogućuje planiranje izvršenja zadatka umjesto da ga pokreće odmah kada je nit dostupna. Također može planirati zadatke s fiksnom stopom ili fiksnim kašnjenjem.
  • ForkJoinPool je posebna ExecutorService za rješavanje zadataka rekurzivnih algoritama. Ako koristite redoviti ThreadPoolExeecuter za rekurzivni algoritam brzo ćete otkriti da su sve vaše teme zauzete čekajući da se završe niže razine rekurzije. The ForkJoinPool implementira takozvani algoritam krađe rada koji mu omogućuje učinkovitiju upotrebu dostupnih niti.

P9. Što je Java memorijski model (Jmm)? Opišite njegovu svrhu i osnovne ideje.

Java memorijski model dio je specifikacije jezika Java opisan u poglavlju 17.4. Određuje kako više niti pristupa zajedničkoj memoriji u istodobnom Java programu i kako promjene podataka jedne niti postaju vidljive ostalim nitima. Iako je prilično kratak i sažet, JMM ga je teško shvatiti bez jake matematičke pozadine.

Potreba za memorijskim modelom proizlazi iz činjenice da način na koji vaš Java kôd pristupa podacima nije kako se to zapravo događa na nižim razinama. Zapisivanje i čitanje memorije može se preurediti ili optimizirati od strane Java kompajlera, JIT kompajlera, pa čak i CPU, pod uvjetom da je vidljivi rezultat tih čitanja i pisanja isti.

To može dovesti do kontra-intuitivnih rezultata kada se vaša aplikacija skalira na više niti, jer većina ovih optimizacija uzima u obzir jednu nit izvršavanja (optimizaciju više nitki i dalje je izuzetno teško implementirati). Još jedan ogroman problem je taj što je memorija u modernim sustavima višeslojna: više jezgri procesora može zadržati neke neisprane podatke u svojim predmemorijama ili međuspremnike za čitanje / pisanje, što također utječe na stanje memorije uočeno od drugih jezgri.

Da bi stvari bile još gore, postojanje različitih arhitektura pristupa memoriji prekršilo je Javino obećanje "piši jednom, trči svugdje". Na sreću programera, JMM navodi neka jamstva na koja se možete osloniti prilikom dizajniranja višenitnih aplikacija. Pridržavanje ovih jamstava pomaže programeru da napiše višenitni kod koji je stabilan i prenosiv između različitih arhitektura.

Glavni pojmovi JMM-a su:

  • Akcije, ovo su radnje među nitima koje jedna nit može izvršiti, a druga nit otkriti, poput čitanja ili pisanja varijabli, zaključavanja / otključavanja monitora i tako dalje
  • Akcije sinkronizacije, određeni podskup radnji, poput čitanja / pisanja a hlapljiv varijabla ili zaključavanje / otključavanje monitora
  • Redoslijed programa (PO), uočljivi ukupni redoslijed radnji unutar jedne niti
  • Nalog za sinkronizaciju (SO), ukupan redoslijed između svih sinkronizacijskih radnji - on mora biti u skladu s programskim redoslijedom, odnosno ako se dvije radnje sinkronizacije dođu jedna prije druge u PO, one se događaju istim redoslijedom u SO
  • sinkronizira-s (SW) odnos između određenih radnji sinkronizacije, poput otključavanja monitora i zaključavanja istog monitora (u drugoj ili istoj niti)
  • Događa se prije reda - kombinira PO sa SW (to se naziva prijelazno zatvaranje u teoriji skupova) stvoriti djelomični poredak svih radnji između niti. Ako jedna radnja događa-prije drugo, tada su rezultati prve akcije vidljivi drugom radnjom (na primjer, upišite varijablu u jednu nit, a pročitajte u drugu)
  • Događa se prije dosljednosti - niz radnji je HB-konzistentan ako svako čitanje promatra ili posljednje upisivanje na to mjesto u redoslijedu "dogodi se prije" ili neko drugo pisanje putem podatkovne utrke
  • Izvršenje - određeni skup uređenih radnji i pravila o dosljednosti

Za zadani program možemo promatrati više različitih izvršenja s različitim ishodima. Ali ako je program ispravno sinkroniziran, tada se čini da su sva njegova izvršenja sekvencijalno dosljedan, što znači da o višenitnom programu možete razmišljati kao o skupu radnji koje se odvijaju u nekom slijedu. To vam štedi probleme pri razmišljanju o preuređivanjima, optimizacijama ili predmemoriranju podataka ispod haube.

Q10. Što je hlapljivo polje i kakva jamstva vrijedi Jmm za takvo polje?

A hlapljiv polje ima posebna svojstva prema Java memorijskom modelu (vidi P9). Čita i piše a hlapljiv varijabla su radnje sinkronizacije, što znači da imaju ukupan poredak (sve niti će poštivati ​​dosljedan redoslijed tih radnji). Čitanje volatilne varijable zajamčeno je za praćenje zadnjeg upisa u ovu varijablu, prema ovom redoslijedu.

Ako imate polje kojem se pristupa iz više niti, a u njega se zapisuje barem jedna nit, trebali biste razmisliti o tome da ga napravite hlapljiv, ili postoji malo jamstvo za ono što bi određena nit čitala iz ovog polja.

Još jedno jamstvo za hlapljiv je atomskost pisanja i čitanja 64-bitnih vrijednosti (dugo i dvostruko). Bez hlapljivog modifikatora, čitanje takvog polja moglo bi promatrati vrijednost djelomično zapisanu drugom nitom.

Q11. Koje su od sljedećih operacija atomske?

  • pisanje ne-hlapljivint;
  • pisanje u a hlapljiv int;
  • pisanje ne-hlapljiv dug;
  • pisanje u hlapljiv dug;
  • povećavajući a hlapljiv dug?

Pisanje na int (32-bitna) varijabla zajamčena je atomska, bez obzira je li to hlapljiv ili ne. A dugo (64-bitna) varijabla mogla bi se napisati u dva odvojena koraka, na primjer, na 32-bitnim arhitekturama, tako da prema zadanim postavkama ne postoji jamstvo atomskosti. Međutim, ako navedete hlapljiv modifikator, a dugo Zajamčeno je da će se varijabli pristupiti atomski.

Operacija prirasta obično se vrši u više koraka (dohvaćanje vrijednosti, promjena i pisanje natrag), tako da nikad nije zajamčeno da je atomska, što je varijabla hlapljiv ili ne. Ako trebate implementirati atomski prirast vrijednosti, trebali biste koristiti klase AtomicInteger, AtomicLong itd.

Q12. Koja posebna jamstva vrijedi Jmm za završna polja klase?

JVM u osnovi to garantira konačni polja klase bit će inicijalizirana prije nego što bilo koja nit dohvati objekt. Bez ovog jamstva, referenca na objekt može se objaviti, tj. Postati vidljiva, u drugoj niti prije nego što se sva polja ovog objekta pokrenu zbog promjena redoslijeda ili drugih optimizacija. To bi moglo dovesti do rijetkog pristupa tim poljima.

Zbog toga biste, prilikom stvaranja nepromjenjivog objekta, uvijek trebali napraviti sva njegova polja konačni, čak i ako im nije moguće pristupiti putem getter metoda.

Q13. Koje je značenje sinkronizirane ključne riječi u definiciji metode? statičke metode? Prije bloka?

The sinkronizirano Ključna riječ prije bloka znači da bilo koja nit koja ulazi u ovaj blok mora steći monitor (objekt u zagradama). Ako je monitor već pribavljen drugom niti, bivša nit ući će u BLOKIRAN stanje i pričekajte dok se monitor ne pusti.

sinkronizirano (objekt) {// ...}

A sinkronizirano metoda instance ima istu semantiku, ali sama instanca djeluje kao monitor.

sinkronizirana void instanceMethod () {// ...}

Za statički sinkronizirani metoda, monitor je Razred objekt koji predstavlja klasu deklariranja.

statička sinkronizirana praznina staticMethod () {// ...}

P14. Ako dvije niti istodobno pozivaju sinkroniziranu metodu na različitim instancama objekata, može li se jedna od ovih niti blokirati? Što ako je metoda statična?

Ako je metoda metoda instance, tada instanca djeluje kao monitor metode. Dvije niti koje pozivaju metodu na različitim instancama stječu različite monitore, pa se niti jedan ne blokira.

Ako je metoda statički, tada je monitor Razred objekt. Za obje niti, monitor je isti, pa će jedan od njih vjerojatno blokirati i pričekati da drugi izađe iz sinkronizirano metoda.

P15. Koja je svrha metoda čekanja, obavještavanja i obavještavanja klase predmeta?

Nit koja je vlasnik monitora objekta (na primjer, nit koja je unijela sinkronizirano odjeljak koji čuva objekt) može nazvati object.wait () da biste privremeno pustili monitor i pružili drugim nitima priliku da nabave monitor. To se, na primjer, može učiniti da bi se pričekalo određeno stanje.

Kada druga nit koja je prikupila monitor ispuni uvjet, može nazvati object.notify () ili object.notifyAll () i otpustite monitor. The obavijestiti metoda budi jednu nit u stanju čekanja, a notifyAll metoda budi sve niti koje čekaju ovaj monitor i svi se natječu za ponovno stjecanje brave.

Sljedeće BlockingQueue Implementacija pokazuje kako više niti radi zajedno putem pričekati-obavijestiti uzorak. Ako mi staviti element u prazan red, sve niti koje su čekale u uzeti metoda probudite se i pokušajte primiti vrijednost. Ako mi staviti element u puni red, staviti metoda čekatis za poziv na dobiti metoda. The dobiti metoda uklanja element i obavještava niti koje čekaju u staviti metoda da red ima prazno mjesto za novu stavku.

javna klasa BlockingQueue {privatni red čekanja = novi LinkedList (); privatno int ograničenje = 10; javno sinkronizirano void put (T stavka) {while (queue.size () == limit) {try {wait (); } catch (InterruptedException e) {}} if (queue.isEmpty ()) {notifyAll (); } queue.add (stavka); } javno sinkronizirano T take () baca InterruptedException {while (queue.isEmpty ()) {try {wait (); } catch (InterruptedException e) {}} if (queue.size () == limit) {notifyAll (); } povratni red.remove (0); }}

Q16. Opišite uvjete zastoja, zastoja i gladi. Opišite moguće uzroke ovih stanja.

Zastoj je uvjet unutar grupe niti koji ne može napredovati jer svaka nit u grupi mora steći neki resurs koji je već stekla druga nit u grupi. Najjednostavniji je slučaj kada dvije niti trebaju zaključati oba dva resursa da bi napredovale, prvi resurs već zaključava jedna nit, a drugi druga. Te niti nikada neće steći zaključavanje oba resursa i stoga nikada neće napredovati.

Livelock je slučaj da više niti reagiraju na uvjete ili događaje koje generiraju sami. Događaj se događa u jednoj niti, a mora ga obraditi druga nit.Tijekom ove obrade događa se novi događaj koji se mora obraditi u prvoj niti i tako dalje. Takve niti su žive i nisu blokirane, ali ipak, ne napreduju, jer međusobno zatrpavaju beskorisnim radom.

Gladovanje je slučaj da nit ne može dobiti resurs jer je druga nit (ili niti) predugo zauzimaju ili imaju veći prioritet. Konac ne može napredovati, a samim tim nije u stanju ispuniti koristan posao.

Q17. Opišite svrhu i slučajeve upotrebe okvira Fork / Join.

Okvir fork / join omogućuje paraleliziranje rekurzivnih algoritama. Glavni problem paraleliziranja rekurzije pomoću nečega poput ThreadPoolExeecuter jest da vam brzo može nestati niti, jer bi svaki rekurzivni korak zahtijevao vlastitu nit, dok bi niti u steku bile u stanju mirovanja i čekale.

Ulazna točka okvira fork / join je ForkJoinPool klasa koja je provedba ExecutorService. Provodi algoritam krađe rada, gdje neaktivne niti pokušavaju "ukrasti" posao iz zauzetih niti. To omogućuje širenje izračuna između različitih niti i napredak uz upotrebu manje niti nego što bi to bilo potrebno s uobičajenim spremištem niti.

Više informacija i uzorci koda za fork / join framework mogu se naći u članku "Vodič za Fork / Join Framework u Javi".

Sljedeći » Pitanja o intervjuu za strukturu klase Java i inicijalizaciju « Prethodna pitanja o intervjuu za sustav tipa Java