Java konkurentno programiranje intervju pitanja, 73 Java više-nitnih pitanja (35.000 reči, 145 ručno crtanih crteža), obavezno čitanje za one koji žele da pobede na intervjuu 👍

Uvod
35.000 reči i 145 ručno crtanih crteža, detaljno objašnjeno 73 najčešća pitanja sa Java više-nitnih intervjua (učinite da učenje nije teško), kada kandidati nauče ova konkurentna programiranja, ovaj put ćećete zaslepiti intervjuesc, mislim da sam siguran (ručni pas).
Prvobitni autor prve izdanje je bio Sanfen e, gost iz Ergova programerskog sveta, a drugo izdanje je Ergovo novo nadogradnje kombinujući iskustva članova sveta sa intervjua + tehnološka škola + PmHub + mydb projekata. pogodnije za brzo učenje za intervju + razumevanje principi.
Svetla verzija je pogodnija za štampanje, što je i način koji mnogi student vole, štampanje učenje je efikasnije.

- januara 2025. godine počeo sam raditi na drugo izdanje ažuriranja.
Za česta pitanja, označiće se poziciju pojavljivanja u „Java intervju vodič“, koja kompanija, koja je originalno pitanje, i dodaće 🌟, sadržaj je jasan; ako želite da uštedite vreme, možete prvo naučiti ova pitanja, što pre možete saznati više o drugoj strani, stotinu bitaka i ne zaboraviti.
Razlikuj kvalitetan odgovor od principa objašnjenje, neka svi znaju zašto i kako, a isto tako mogu efikasno odgovoriti na intervjuu.
Kombinuj projekat (Tehnološka škola, pmhub) da organizuješ jezik, neka intervjuesc maksimalno oseti tvoju iskrenost, a ne mehaničko učenje.
Popravljeno probleme iz prvog izdanja, uključujući povratne informacije članova sveta, komentare iz sekcije za komentare na veb sajtu, kao i probleme iz GitHub repozitorijuma, neka ovaj intervju vodič bude savršeniji.
Dodao neke offer-e koje su članovi Ergova programerskog sveta dobili, zahvalnice na intervju pobeda, kao i priznanja za izmjenu životopisa, kako bih motivisao sve, dao više samopouzdanja.
Optimizovan raspored, dodao ručno crtane crteže, reorganizovao odgovore, više govorni jezik, bliže očekivanjima intervjuesca.

Budući da PDF ne može da se ažurira sam, prijatelji koji žele najnoviju verziju mogu pretražiti [Chenmo Wang Er] na WeChatu, ili skenirati/duže pritisnuti sledeći QR kod, prati Ergov javni nalog, odgovoriti [222] može povući najnoviju verziju.
[Zatvoreno zbog uklanjanja monetarizacije - QR kod javnog naloga]
Naravno, dozvoli mi malo sebičnosti, PDF verzija sveta će biti mesec dana ranija od javnog naloga, jer su članovi sveta već platili, moram da im prvo omogućim malo benefiti. Verujem da svi mogu da razumeju, budući da je online verzija besplatna, CDN, serveri, domeni, OSS itd. sve košta.
Ne spominjući moje vreme i energiju, ako smatrate da je pomoćno, dajte reputaciju, neka vaši kolege i kolege takođe mogu da koriste.

Stavio sam Ergov napredni Java put, JVM napredni put, konkurentno programiranje napredni put, kao i sve verzije intervju pobeda, obuhvataju Java osnove, Java kolekcije, Java konkurentnost, JVM, Spring, MyBatis, računarske mreže, operativni sistemi, MySQL, Redis, RocketMQ, distribuirane sisteme, mikro servise, dizajn obrasce, Linux itd. 16 velikih tema, ukupno više od 400.000 reči, 2000+ ručno crtanih crteža, zaista iskren.
Prikažimo PDF tamne verzije, raspored je jasan, font je elegantan, pogodniji za noćno čitanje, noću će biti ugodnije.

Osnove
1.Kakva je razlika između paralelnog i konkurtnog?
- Paralelno je više-zadatačno procesiranje na više-jezgrenom CPU, više zadataka se istovremeno izvršava istovremeno.
- Konkurentno je više-zadatačno procesiranje na jedno-jezgrenom CPU, više zadataka se istovremeno izvršava naizmenično u istom vremenskom periodu, realizuje se kroz rotiranje vremenskih intervala, rešavaće usko grlo zadataka intenzivnih I/O operacija.

Na primer, kao što idemo u menzu da jedem, paralelno je svaka osoba ima jednu tetku, istovremeno jede; a konkurentno je jedna tetka, redom jede svakoj osobi, ako neko ko voli da se šeta, tetka će pozvati sledeću osobu, tako da može povećati efikasnost jedenja u menzi.

Kako razumijete niti bezbednosti?
Preporučeno čitanje: Kakve probleme donosi više niti?
Ako blok koda ili metod može biti istovremeno izvršen od više niti, i još uvek može tačno da procesira deljene podatke, onda je taj blok koda ili metod nit-bezbedan.
Možete osigurati nit bezbednosti iz tri elementa:
1) Atomnost: Operacija se ili potpuno izvršava, ili se uopšte ne izvršava, neće se pojaviti intermedijerno stanje.

Možete koristiti sinhronizacionu ključnu reč synchronized ili atomične operacije, poput AtomicInteger, da garantujete atomnost.
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // atomična operacija2) Vidljivost: Kada jedna nit izmeni deljenu promenljivu, druge niti mogu odmah videti promenu.

Možete koristiti volatile ključnu reč da garantujete vidljivost.
private volatile String itwanger = "Chenmo Wang Er";3) Redosled: Treba osigurati da nit neće prestat izvršavati zbog problema poput mrtvog zamka, izgladnjivanja, živog zamka itd.

- Java intervju vodič uključuje Huaweijevog OD intervju kandidat 1 prvo lice originalno pitanje: kakvo razumijevanje više-nitnog programiranja?
- Java intervju vodič uključuje Kuaišou intervju kandidat 1 odeljenje glavne stanice tehnološkog odseka intervju originalno pitanje: kako razumijete nit bezbednosti?
memo: 22. januara 2025. izmenjeno do ovde.
2.🌟Kakva je razlika između procesa i niti?
Preporučeno čitanje:Kakva je razlika između procesa i niti?
Prost rečeno, procesi su aplikacije koje pokrećemo na računaru. To je najmanja jedinica dodele resursa operativnog sistema.
Nit je nezavisna izvršna jedinica u procesu. Više niti može deliti resurse istog procesa, poput memorije; svaka nit ima svoj nezavisni stek i registre.

Kako razumijete korutine?
Korutine se smatraju lakšim konkurtnim jedinicama od niti, mogu realizovati konkurtno izvršavanje u jednoj niti, eksplicitno raspoređuju od strane programera.
Korutine se raspoređuju u korisničkom režimu, izbegavaju troškove prelaska u jezgro pri promeni niti.
Java sama ne podržava korutine, možemo koristiti Quasar, Kotlin i druge okvire da realizujemo korutine.
fun main() = runBlocking {
launch {
delay(1000L)
println("Svet!")
}
println("Zdravo,")
}Kako niti komuniciraju?
U principu se može realizovati kroz dva metoda: prenos poruka i deljena memorija. Java koristi model konkurtnosti deljene memorije.
Ovaj model se zove Java memorijski model, skraćeno JMM, on određuje kada upis jedne niti u deljenu promenljivu postaje vidljiv drugoj niti. Naravno, lokalna memorija je apstraktni koncept JMM-a, ne postoji stvarno.
Jednom rečenjom sažeto: deljene promenljive se čuvaju u glavnoj memoriji, privatna lokalna memorija svake niti čuva kopiju te deljene promenljive.

Ako se nit A i nit B trebaju komunicirati, potrebno je dva koraka:
- Nit A osveži kopiju deljene promenljive iz lokalne memorije A u glavnu memoriju.
- Nit B čita deljenu promenljivu koju je nit A osvežila iz glavne memorije, a zatim je sinhronizuje u svoju kopiju deljene promenljive.

- Java intervju vodič uključuje ByteDance komercijalno prvo lice originalno pitanje: razlika između procesa i niti, razlika između deljene memorije niti i deljene memorije procesa
- Java intervju vodič uključuje Xiaomi prolećni regrutaciju kandidat K prvo lice intervju originalno pitanje: razlika između korutina, niti i procesa
- Java intervju vodič uključuje ByteDance intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: kakva je razlika između niti i procesa?
- Java intervju vodič uključuje Huaweijev OD intervju kandidat 1 prvo lice intervju originalno pitanje: kakvo razumijevanje više-nitnog programiranja?
- Java intervju vodič uključuje Meituan intervju kandidat 2 Java pozadina tehnologija prvo lice intervju originalno pitanje: razlika između procesa i niti?
- Java intervju vodič uključuje Huawei intervju kandidat 9 Java opšta razvojna softvera prvo lice intervju originalno pitanje: razlika između procesa i niti
- Java intervju vodič uključuje zbirku malih kompanija intervju Haopuiankao testiranje intervju kandidat 3 testiranje prvo lice intervju originalno pitanje: razlika između procesa i niti
- Java intervju vodič uključuje China Merchants Bank intervju kandidat 6 China Merchants Bank Network Technology intervju originalno pitanje: kakva je razlika između procesa i niti?
- Java intervju vodič uključuje Yongyou intervju originalno pitanje: razlika između niti i procesa
- Java intervju vodič uključuje vivo intervju kandidat 10 tehnologija prvo lice intervju originalno pitanje: koncept niti, koja stanja ima nit
- Java intervju vodič uključuje Hikvision kandidat 4 intervju originalno pitanje: razumijevanje korutina, zašto korutine troše manje resursa od niti
memo: 17. avgusta 2025. izmenjeno do ovode.Dok sam danas pomagao članu sveta da promeni životopis, dobio sam povratnu informaciju od njega: prošli put me je trudio da mu promenim životopis, takođe je uspešno pronašao praksu, prolećna regrutacija se približava, nadam se da mu mogu ponovo pomoći da pogleda iskustvo prakse. Hvala članu sveta na svakoj povratnoj informaciji.

3.🌟Koliko načina kreiranja niti postoji?
Preporučeno čitanje: Cimer je igrao jednog Kralja i naučio Java više niti
Tri načina: nasleđivanje Thread klase, implementacija Runnable interfejsa, implementacija Callable interfejsa.

Prvi način zahteva redefinisanje run() metoda roditeljske Thread klase i pozivanje start() metoda za pokretanje niti.
class ThreadTask extends Thread {
public void run() {
System.out.println("Nakon što sam pročitao Ergov napredni Java put, uspeo sam!");
}
public static void main(String[] args) {
ThreadTask task = new ThreadTask();
task.start();
}
}Mana ovog metoda je što ako ThreadTask već nasleđuje jednu klasu, ne može naslediti Thread klasu, jer Java ne podržava višestruko nasleđivanje.
Drugi način zahteva redefinisanje run() metoda Runnable interfejsa i prosleđivanje objekta implementacione klase kao parametar konstruktoru Thread objekta, na kraju pozivanje start() metoda za pokretanje niti.
class RunnableTask implements Runnable {
public void run() {
System.out.println("Nakon što sam pročitao Ergov napredni Java put, uspeo sam!");
}
public static void main(String[] args) {
RunnableTask task = new RunnableTask();
Thread thread = new Thread(task);
thread.start();
}
}Prednost ovog metoda je što može izbeći ograničenje jednostručnog nasleđivanja Java, a više odgovara objektno-orijentisanom programiranju, jer Runnable interfejs razdvaja kod zadataka od kod kontrole niti.
Treći način zahteva redefinisanje call() metoda Callable interfejsa, zatim kreiranje FutureTask objekta, parametar je objekat implementacione klase Callable; zatim kreiranje Thread objekta, parametar je FutureTask objekat, na kraju pozivanje start() metoda za pokretanje niti.
class CallableTask implements Callable<String> {
public String call() {
return "Nakon što sam pročitao Ergov napredni Java put, uspeo sam!";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTask task = new CallableTask();
FutureTask<String> futureTask = new FutureTask<>(task);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}Prednost ovog metoda je što može dobiti rezultat izvršavanja niti.
Koliko niti može sistem sa 8GB memorije najviše kreirati?
Preporučeno čitanje: Duboko razumevanje JVM runtime područja podataka
Teorijski oko 8000.
Pri kreiranju niti, barem mora dodeliti jedan virtuelni mašinski stek, u 64-bitnom operativnom sistemu, podrazumevana veličina je 1M, stoga jedna nit otprilike treba 1M memorije.
Ali JVM, sam operativni sistem zauzima određeni prostor memorije, stvari se zapravo može kreirati mnogo manje niti od 8000.
Detaljno objašnjenje.
Možete koristiti java -XX:+PrintFlagsFinal -version | grep ThreadStackSize komandu da vidite podrazumevanu veličinu JVM steka.

ThreadStackSize je u KB, to je podrazumevana veličina JVM steka 1024 KB, odnosno 1M.
Kada pokrenete Java program, koje niti postoje u njemu?
Prvo je glavna nit, to je ulaz izvršavanja programa.
Zatim nit za sakupljanje otpada, to je pozadinska nit, odgovorna za sakupljanje neupotrebljenih objekata.
I kompajlerske niti, poput JIT, odgovorne su za kompajliranje dela toplih koda i smeštanje u codeCache.

Možete koristiti sledeći kod za testiranje:
class ThreadLister {
public static void main(String[] args) {
// Dobiti stekove traga svih niti
Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
for (Thread thread : threads.keySet()) {
System.out.println("Nit: " + thread.getName() + " (ID=" + thread.getId() + ")");
}
}
}Rezultat je sledeći:
Nit: Monitor Ctrl-Break (ID=5)
Nit: Reference Handler (ID=2)
Nit: main (ID=1)
Nit: Signal Dispatcher (ID=4)
Nit: Finalizer (ID=3)Jednostavno objašnjenje:
Nit: main (ID=1)- glavna nit, koju JVM kreira pri pokretanju Java programa.Nit: Reference Handler (ID=2)- ova nit se koristi za procesuiranje referenci, poput mekih, slabih i virtuelnih referenci. Odgovorna je za čišćenje objekata koje je JVM vratio.Nit: Finalizer (ID=3)- finalizerska nit, odgovorna za pozivanje finalize metoda objekta. Pre nego što sakupljač otpada označi objekat kao moguć za vracanje, ova nit izvršava njegovu finalize metodu za oslobađanje određenih resursa.Nit: Signal Dispatcher (ID=4)- nit za raspoređivanje signala, procesuira signale iz operativnog sistema i prosleđuje ih JVM za dalje procesuiranje, poput odgovora na prekide, zaustavljanje itd.Nit: Monitor Ctrl-Break (ID=5)- monitorska nit, obično je kreira neki specifični IDE za monitoring i upravljanje izvršavanjem programa ili procesuiranje prekida u procesu razvoja.
Da li ste koristili više niti? U kojim scenama u kodu?
Često koristim, batch procesuiranje podataka, asinhrone zadatke, planirane zadatke, sve zahteva više niti.
Na primer, u tehnološkoj školi učitavanje sadržaja naslovne stranice, koristio sam više niti paralelno za učitavanje različitih modula, poboljšavajući brzinu odziva stranice.

- Java intervju vodič uključuje ByteDance intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: koliko metoda implementacije niti postoji?
- Java intervju vodič uključuje Agricultural Bank of China kandidat 1 intervju originalno pitanje: metode implementacije niti i razlike
- Java intervju vodič uključuje Agricultural Bank of China intervju kandidat 3 Java pozadina intervju originalno pitanje: recite mi metode kreiranja niti
- Java intervju vodič uključuje zbirku malih kompanija intervju kandidat 1 Java pozadina intervju originalno pitanje: metodi kreiranja niti? Koja je razlika između Runable i Callable?
- Java intervju vodič uključuje Alibaba intervju kandidat 5 Ali Mama Java pozadina tehnologija prvo lice intervju originalno pitanje: koliko niti može sistem sa 8G memorije najviše kreirati? (čudno pitanje, odgovorio sam nešto o pcb, strani tablici, virtuelnom mašinskom steku itd.) Kada pokrenete Java program, koje niti postoje u njemu?
- Java intervju vodič uključuje China Merchants Bank intervju kandidat 6 China Merchants Bank Network Technology intervju originalno pitanje: kako kreirati nit?
- Java intervju vodič uključuje Baidu intervju kandidat 1 Wenxin Yiyan 25 praksak Java pozadina intervju originalno pitanje: kako Java kreira nit? Svaki put kreira novu nit za realizaciju asinhrone operacije, vrlo je dosadno, da li znate za nitni bazen?
- Java intervju vodič uključuje Meituan intervju kandidat 4 prvo lice intervju originalno pitanje: kako koristite više niti u svakodnevnom životu
memo: 26. septembra 2025. izmenjeno do ovode. Danas je član sveta u svetu javio da je dobio ponudu od ByteDance, hvalio Ergovoj intervju pobeda.

4.🌟Pozivanje start metoda će izvršiti run metod, zašto ne pozivati run metod direktno?
Pozivanje start() će kreirati novu nit i asinhrono izvršiti kod u run() metodu.
Direktno pozivanje run() metoda je samo obična sinhrona metoda poziva, sav kod se izvršava u trenutnoj niti, ne kreira novu nit. Nema kreiranja nove niti, ne može postići cilj više-nitne konkurentnosti.
Klikni kod da osetiš.
class MyThread extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // tačan način, kreira novu nit i izvršava run() u novoj niti
t1.run(); // samo izvršava run() u glavnoj niti, ne kreira novu nit
}
}Pogledaj rezultat:
main
Thread-0To znači, pozivanje start() metoda će obavestiti JVM da pozove donji mehanizam raspoređivanja niti za pokretanje nove niti.

Nakon pozivanja start(), nit ulazi u stanje spremnosti, čeka raspoređivanje operativnog sistema; jednom raspoređena za izvršenje, nit će izvršiti kod u run() metodu.
- Java intervju vodič uključuje zbirku malih kompanija intervju kandidat 1 Java pozadina intervju originalno pitanje: za pokretanje niti je run() ili start()?
- Java intervju vodič uključuje Baidu intervju kandidat 1 Wenxin Yiyan 25 praksak Java pozadina intervju originalno pitanje: kako Java pokreće više niti, koji metodi postoje?
- Ergov programerski svet član Zhen Yun Mian Meituan AI intervju originalno pitanje: kakva je razlika između start i run metoda u nitnim operacijama Java
memo: 26. januara 2025. izmenjeno do ovode.
5.Koje metode se obično koriste za raspoređivanje niti?
Na primer, start metod se koristi za pokretanje niti i omogućava operativnom sistemu da rasporedi izvršenje; sleep metod se koristi da trenutna nit odmara određeno vreme; wait metod će trenutnu nit staviti u stanje čekanja, notify će probuditi jednu čekajuću nit.

Recite mi o wait i notify metodima?
Kada nit A pozove wait metod deljenog objekta, nit A će biti blokirana i suspendovana, dok se ne desi:
- Nit B pozove notify metod ili notifyAll metod deljenog objekta;
- Druge niti pozovu interrupt metod niti A, uzrokujući da nit A baci InterruptedException izuzetak.
Nakon što nit A pozove wait(timeout) metod deljenog objekta, ako nije probuđena od drugih niti u određenom timeout vremenu, metod će se vratiti zbog isteka vremena.
Kada nit A pozove notify metod deljenog objekta, probudićete jednu nit suspendovanu pozivom wait serije metoda na tom deljenom objektu.
Može biti više niti koje čekaju na deljenom objektu, konkretno koja će se probuditi je nasumično.
Ako pozovete notifyAll metod, probudićete sve niti suspendovane pozivom wait serije metoda na toj deljenoj promenljivoj.
Recite mi o sleep metodu?
Nakon što nit A pozove sleep metodu Thread klase, nit A će privremeno prepustiti pravo izvršavanja za određeno vreme.
Nakon što navedeno vreme za spavanje protekne, metod će se vratiti normalno, zatim učestvovati u CPU raspoređivanju, nakon dobijanja CPU resursa može nastaviti izvršavanje.
Recite mi o yield metodu?
Svrha yield() metoda je da trenutna nit prepusti pravo korišćenja CPU i vrati se u stanje spremnosti. Ali raspoređivač niti može ignorisati.
Recite mi o interrupt metodu?
Preporučeno čitanje: interrupt metod
interrupt() metod se koristi za obaveštavanje niti da prestane, ali neće direktno prekinuti nit, potrebno je da nit sama obradi prekidni flag.
Često se koristi sa isInterrupted() ili Thread.interrupted().
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Izvršavanje");
}
System.out.println("Prekinuto");
});
thread.start();
thread.interrupt(); // prekini nitRecite mi o stop metodu?
stop metod se koristi za prisilno zaustavljanje niti, trenutno je u stanju zastarelosti, jer stop metod može osloboditi zaključavanje u nekonzistentnom stanju, narušavajući konzistentnost objekta.

- Java intervju vodič uključuje FanRuan kandidat 3 Java pozadina prvo lice originalno pitanje: kako zaustaviti nit, kakva je razlika između interrupt i stop
memo: 27. januara 2025. izmenjeno do ovode.
6.🌟Koliko stanja niti postoji?
6 vrsta.
new predstavlja nit je kreirana ali nije pokrenuta; runnable predstavlja nit je u stanju spremnosti ili se izvršava, raspoređuje operativni sistem; blocked predstavlja nit je blokirana, čeka da dobije zaključavanje; waiting predstavlja nit čeka obaveštenje ili prekid drugih niti; timed_waiting predstavlja nit će čekati određeno vreme, nakon isteka automatski se oporavi; terminated predstavlja nit je završila izvršenje, životni ciklus je završen.

To znači, životni ciklus niti može se podeliti u pet glavnih faza: kreiranje, spremnost, izvršavanje, blokiranje i završetak. Nit će se prebacivati između ovih faza tokom izvršavanja na osnovu promene stanja.
class ThreadStateExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000); // TIMED_WAITING
synchronized (ThreadStateExample.class) {
ThreadStateExample.class.wait(); // WAITING
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
System.out.println("Stanje nakon kreiranja: " + thread.getState()); // NEW
thread.start();
System.out.println("Stanje nakon starta: " + thread.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println("Stanje dok spava: " + thread.getState()); // TIMED_WAITING
synchronized (ThreadStateExample.class) {
ThreadStateExample.class.notify(); // probudi nit
}
thread.join();
System.out.println("Stanje nakon završetka: " + thread.getState()); // TERMINATED
}
}Sažetak u tabeli:
| Stanje | Objasnjenje |
|---|---|
| NEW | Kada je nit kreirana, poput new Thread(), nalazi se u stanju kreiranja. U ovom stanju, nit je dodeljena neophodna resursa, ali nije još počela izvršavanje. |
| RUNNABLE | Kada se pozove start() metod niti, nit ulazi u stanje mogućnosti izvršavanja. U ovom stanju, nit se može izvršavati ili može čekati na dobijanje CPU vremenskog intervala, zavisno od strategije raspoređivanja niti. |
| BLOCKED | Nit ulazi u blokirano stanje kada pokušava da dobije zaključavanje za ulazak u sinhronizovani blok/metod, ako zaključavanje druge niti drži, nit će ostati u blokirano stanju dok ne dobije zaključavanje. |
| WAITING | Nit ulazi u stanje čekanja zato što je pozivala jedan od sledećih metoda: Object.wait() ili LockSupport.park(). U stanju čekanja, nit mora druge niti eksplicitno da probude, inače se neće automatski izvršiti. |
| TIME_WAITING | Kada nit pozove metod sa parametrom timeout, poput Thread.sleep(long millis), Object.wait(long timeout) ili LockSupport.parkNanos(), ona će ući u stanje čekanja s vremenom. Nakon što navedeno vreme čekanja protekne, nit će se automatski vratiti u stanje mogućnosti izvršavanja. |
| TERMINATED | Kada se run() metod niti završi izvršenjem, ili zato što je nezahvaćeni izuzetak prekinuo izvršavanje, nit ulazi u završno stanje. Jednom nit završi, njen životni ciklus se završava, ne može se ponovo pokrenuti. |
Kako prisilno prekinuti nit?
Prvi korak, pozovite interrupt() metod niti, zatražite prekid niti.
Drugi korak, u run() metodu niti proverite stanje prekida, ako je nit prekinuta, izađite iz niti.
class MyTask implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("Izvršavanje...");
Thread.sleep(1000); // simulacija rada
} catch (InterruptedException e) {
// nakon hvatanja prekidnog izuzetka, resetujte stanje prekida
Thread.currentThread().interrupt();
System.out.println("Nit prekinuta, izlazim...");
break;
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyTask());
thread.start();
Thread.sleep(3000); // glavna nit čeka 3 sekunde
thread.interrupt(); // zatraži prekid niti
}
}Rezultat prekida:

- Java intervju vodič uključuje China Merchants Bank intervju kandidat 6 China Merchants Bank Network Technology intervju originalno pitanje: životni ciklus i stanja niti?
- Java intervju vodič uključuje Kuaishou kandidat 2 prvo lice intervju originalno pitanje: koja stanja ima nit?
- Java intervju vodič uključuje OPPO intervju kandidat 1 intervju originalno pitanje: životni ciklus Java niti
- Java intervju vodič uključuje kandidat D Xiaomi prvo lice originalno pitanje: životni ciklus niti
7.Šta je kontekstualna promena niti?
Kontekstualna promena niti odnosi se na proces kada CPU prelazi iz jedne niti u drugu za izvršavanje.
U procesu promene niti, CPU treba sačuvati trenutno stanje izvršavanja niti i učitati kontekst sledeće niti.
Razlog za to je što CPU u istom trenutku može izvršavati samo jednu nit, da bi se realizovalo više-nitno konkurentno izvršavanje, potrebno je stalno prebacivati između više niti.

Da bi korisnici osećali da se više niti izvršava istovremeno, dodela CPU resursa koristi metodu rotacije vremenskih intervala, nit zauzima CPU za izvršavanje zadataka unutar vremenskog intervala. Kada nit iskoristi vremenski interval, prepustiće CPU da druge niti zauzmu.

Može li nit biti raspoređena na više jezgara?
Više-jezgarni procesori pružaju mogućnost paralelnog izvršavanja više niti. Svako jezgro može nezavisno izvršavati jednu ili više niti, raspoređivač zadataka operativnog sistema će na osnovu strategija i algoritama, poput raspoređivanja po prioritetu, rotacionog raspoređivanja itd., odlučiti koja nit kada i na kojem jezgru će se izvršavati.
- Java intervju vodič uključuje ByteDance kandidat 7 Java pozadina praksak prvo lice originalno pitanje: može li nit biti raspoređena na više jezgara?
- Java intervju vodič uključuje Ctrip intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: kontekstualna promena niti (odgovorio sam trenutke prelaska jezgro i korisničkog režima, i koje sadržaje treba učitati pri promeni)
8.Šta znate za demon niti?
Znam, demonska nit je specifična nit, njena uloga je da pruža usluge drugim nitima.
Java niti su podeljene u dve kategorije, jedna su demonske niti, druga su korisničke niti.
JVM će pozvati main metod pri pokretanju, nit u kojoj se nalazi main metod je korisnička nit. U JVM, istovremeno su pokrenute i mnoge demonske niti, poput niti za sakupljanje otpada.
Koja je razlika između demonskih i korisničkih niti?
Jedna razlika je u tome što kada se poslednja ne-demonska nit završi, JVM će normalno izaći, bez obzira na to da li trenutno postoje demonske niti, to jest da li će demonske niti završiti ne utiče na JVM izlaz.
Drugo rečeno, samo dok postoji jedna korisnička nit koja nije završila, u normalnim uslovima JVM neće izaći.
9.Koje metode komunikacije postoje između niti?
Postoji više načina za prenos informacija između niti, poput korišćenja volatile i synchronized ključnih reči za deljene objekte, korišćenje wait() i notify() metoda za realizaciju modela proizvođač-potrošač, korišćenje Exchanger za razmenu podataka, korišćenje Condition za koordinaciju niti itd.
Jednostavno recite mi o korišćenju volatile i synchronized?
Više niti može pristupati i menjati isti objekat kroz volatile i synchronized ključne reči, time realizujući prenos informacija.
Ključna reč volatile može se koristiti za modifikovanje članskih promenljivih, obaveštava program da svaki pristup toj promenljivoj mora dobiti iz deljene memorije i sinhronizovati se nazad u deljenu memoriju, garantujući vidljivost pristupa svih niti toj promenljivoj.
Ključna reč synchronized može modifikovati metode ili sinhronizovane blokove koda, osiguravajući da više niti u istom trenutku samo jedna nit izvršava metod ili blok koda.
class SharedObject {
private String message;
private boolean hasMessage = false;
public synchronized void writeMessage(String message) {
while (hasMessage) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.message = message;
hasMessage = true;
notifyAll();
}
public synchronized String readMessage() {
while (!hasMessage) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
hasMessage = false;
notifyAll();
return message;
}
}
public class Main {
public static void main(String[] args) {
SharedObject sharedObject = new SharedObject();
Thread writer = new Thread(() -> {
sharedObject.writeMessage("Zdravo od pisca!");
});
Thread reader = new Thread(() -> {
String message = sharedObject.readMessage();
System.out.println("Čitač primio: " + message);
});
writer.start();
reader.start();
}
}Znate li kako se koriste wait() i notify() metodi?
Kada nit pozove wait() metod deljenog objekta, ona će ući u bazen čekanja tog objekta, oslobađajući već držano zaključavanje, ulazeći u stanje čekanja.
Kada nit pozove notify() metod, ona će probuditi jednu nit koja čeka u bazenu čekanja tog objekta, omogućujući joj da uđe u bazen zaključavanja, čekajući da dobije zaključavanje.
class MessageBox {
private String message;
private boolean empty = true;
public synchronized void produce(String message) {
while (!empty) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
empty = false;
this.message = message;
notifyAll();
}
public synchronized String consume() {
while (empty) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
empty = true;
notifyAll();
return message;
}
}
public class Main {
public static void main(String[] args) {
MessageBox box = new MessageBox();
Thread producer = new Thread(() -> {
box.produce("Poruka od proizvođača");
});
Thread consumer = new Thread(() -> {
String message = box.consume();
System.out.println("Potrošač primio: " + message);
});
producer.start();
consumer.start();
}
}Condition takođe pruža slične metode, await() je odgovoran za blokiranje, signal() i signalAll() su odgovorni za obaveštenje.
Obično se koristi sa zaključavanjem ReentrantLock, pružajući nitima mehanizam čekanja da određeni uslov postane istinitim, i dopuštajući drugim nitima da obaveste čekajuću nit kada se taj uslov promeni.
Znate li kako se koristi Exchanger?
Exchanger je tačka sinhronizacije, može razmenjivati podatke između dve niti. Jedna nit poziva exchange() metod, prenosi podatke drugoj niti, istovremeno prima podatke druge niti.
class Main {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
Thread thread1 = new Thread(() -> {
try {
String message = "Poruka od nit1";
String response = exchanger.exchange(message);
System.out.println("Thread1 primio: " + response);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread thread2 = new Thread(() -> {
try {
String message = "Poruka od nit2";
String response = exchanger.exchange(message);
System.out.println("Thread2 primio: " + response);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread1.start();
thread2.start();
}
}Znate li kako se koristi CompletableFuture?
CompletableFuture je klasa uvedena u Java 8, podržava asinhrono programiranje, dopušta nitima da prenose rezultate drugim nitima nakon završetka izračuna.
class Main {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// simulacija dugog trajanja izračuna
return "Poruka od CompletableFuture";
});
future.thenAccept(message -> {
System.out.println("Primljeno: " + message);
});
}
}
- Java intervju vodič uključuje Huawei OD intervju se pojavilo ovo originalno pitanje.
- Java intervju vodič uključuje Alibaba intervju kandidat 1 Xianyu pozadina prvo lice originalno pitanje: kako se prenose informacije između niti?
- Java intervju vodič uključuje Li Xiang automobil intervju kandidat 2 prvo lice intervju originalno pitanje: koji metodi komunikacije postoje u nitima? koji metodi komunikacije postoje između niti?
memo: 28. januara 2025. izmenjeno do ovode.
10.🌟Recite mi razliku između sleep i wait? (dopuna)
Dodato 21. marta 2024.
sleep će trenutnu nit staviti u stanje spavanja, ne treba da dobije zaključavanje objekta, pripada Thread klasi; wait će nit koja je dobila zaključavanje objekta staviti u stanje čekanja, treba unapred da dobije zaključavanje objekta, pripada Object klasi.
Detaljno objašnjenje.
- Različite klase
sleep()metod pripada isključivoThreadklasi.wait()metod pripada isključivoObjectklasi.
- Različito ponašanje zaključavanja
Ako nit pozove sleep metod dok drži zaključavanje određenog objekta, i dalje će držati to zaključavanje tokom spavanja.
class SleepDoesNotReleaseLock {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread sleepingThread = new Thread(() -> {
synchronized (lock) {
System.out.println("Nit 1 će nastaviti da drži zaključavanje i ući u stanje spavanja");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Nit 1 se probudila i oslobodila zaključavanje");
}
});
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
System.out.println("Nit 2 ulazi u sinhronizovani blok koda");
}
});
sleepingThread.start();
Thread.sleep(1000);
waitingThread.start();
}
}Izlazni rezultat:
Nit 1 će nastaviti da drži zaključavanje i ući u stanje spavanja
Nit 1 se probudila i oslobodila zaključavanje
Nit 2 ulazi u sinhronizovani blok kodaIz izlaza možemo videti da waitingThread mora čekati da sleepingThread završi spavanje pre nego što može ući u sinhronizovani blok koda.
A kada nit izvrši wait metod, oslobađa zaključavanje objekta koje drži, stoga druge niti takođe mogu dobiti zaključavanje tog objekta.
class WaitReleasesLock {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Nit 1 drži zaključavanje, sprema se da čeka 5 sekundi");
lock.wait(5000);
System.out.println("Nit 1 se probudila i izlazi iz sinhronizovanog bloka koda");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread notifyingThread = new Thread(() -> {
synchronized (lock) {
System.out.println("Nit 2 pokušava da probudi čekajuću nit");
lock.notify();
System.out.println("Nit 2 je završila notify");
}
});
waitingThread.start();
Thread.sleep(1000);
notifyingThread.start();
}
}Izlazni rezultat:
Nit 1 drži zaključavanje, sprema se da čeka 5 sekundi
Nit 2 pokušava da probudi čekajuću nit
Nit 2 je završila notify
Nit 1 se probudila i izlazi iz sinhronizovanog bloka kodaOvo pokazuje da waitingThread zaista oslobađa zaključavanje nakon pozivanja wait.
- Različiti uslovi korišćenja
sleep()metod se može pozvati bilo gde.wait()metod se mora pozvati u sinhronizovanom bloku koda ili sinhronizovanom metodu, jer je preduvlove pozivawait()metoda da trenutna nit mora držati zaključavanje objekta. Inače će bacitiIllegalMonitorStateExceptionizuzetak.

- Različiti načini buđenja
- Nakon poziva sleep metoda, nit će ući u TIMED_WAITING stanje, odnosno pauzira izvršavanje za navedeno vreme. Kada navedeno vreme protekne, nit će automatski vratiti u RUNNABLE stanje, čekajući da je CPU ponovo rasporedi za izvršavanje.
- Nakon poziva wait metoda, nit će ući u WAITING stanje, dok druga nit na istom objektu pozove notify ili notifyAll metod, nit će preći iz WAITING stanja u RUNNABLE stanje, spremna da ponovo dobije pravo izvršavanja CPU.
Hajde da kroz kod još jednom osetimo razliku između sleep() i wait() u korišćenju, prvo pogledajmo sleep():
class SleepExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Nit sprema da spava 2 sekunde");
try {
Thread.sleep(2000); // nit će spavati 2 sekunde
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Nit se probudila");
});
thread.start();
}
}Pogledajmo wait():
class WaitExample {
public static void main(String[] args) {
final Object lock = new Object();
Thread thread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Nit sprema da čeka 2 sekunde");
lock.wait(2000); // nit će čekati 2 sekunde, ili dok druga nit ne pozove lock.notify()/notifyAll()
System.out.println("Nit je završila čekanje");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
- Java intervju vodič uključuje Tencent Java pozadina praksak prvo lice originalno pitanje: recite mi razliku između sleep i wait
- Ergov programerski svet član Zhen Yun Mian Meituan AI intervju originalno pitanje: objasnite glavnu razliku između sleep i wait metoda u Java nitima? Kakav uticaj ima na stanje niti pri korišćenju
- Java intervju vodič uključuje Kuaishou kandidat 2 prvo lice intervju originalno pitanje: koje je stanje kada pozovete wait() metod, kakva je razlika između sleep i wait?
- Java intervju vodič uključuje kandidat D Xiaomi prvo lice originalno pitanje: razlika između sleep i wait
memo: 29. januara 2025. izmenjeno do ovode.
11.🌟Kako garantovati nit bezbednosti? (dopuna)
Dodato 1. maja 2024.
Nit bezbednost se odnosi na to da u konkurtnoj okolini, kada više niti pristupa deljenim resursima, program može tačno izvršiti, neće se pojaviti problem nekonzistentnosti podataka.
Da bi se garantovala nit bezbednost, može se koristiti synchronized ključna reč za zaključavanje metoda, za zaključavanje blokova koda. Nit prilikom izvršavanja sinhronizovanog metoda, sinhronizovanog bloka koda, dobija klasno zaključavanje ili objektno zaključavanje, druge niti će biti blokirane i čekati zaključavanje.
Ako je potrebna finija kontrola zaključavanja, može se koristiti ReentrantLock konkurentno rekurzivno zaključavanje itd.
Ako je potrebno garantovati memorijsku vidljivost promenljivih, može se koristiti volatile ključna reč.
Za jednostavne atomične operacije promenljivih, mogu se koristiti Atomic atomične klase.
Za podatke nezavisne od niti, može se koristiti ThreadLocal da svakoj niti pruži nezavisnu kopiju promenljive.
Za mesta koja zahtevaju konkurentne kontejnere, mogu se koristiti ConcurrentHashMap, CopyOnWriteArrayList itd.
Ima int promenljiva 0, deset niti se redom izvršava ++ operaciju (ciklus 10000 puta), rezultat je veći od 100.000 ili manji i jednak 100.000, zašto?
U ovom scenariju, konačan rezultat će biti manji od 100.000, razlog je što u okolini više niti, ++ operacija nije atomična operacija, već je podeljena u tri koraka: čitanje, dodavanje 1, pisanje nazad.
- Čitanje vrednosti promenljive.
- Dodavanje 1 pročitanoj vrednosti.
- Pisanje rezultata nazad u promenljivu.
Na taj način, više niti može pročitati istu vrednost, zatim dodati 1 toj vrednosti, na kraju rezultat je manji od 100.000.
Detaljno objašnjenje.
Više niti pri konkurtnom izvršavanju ++ operacije, mogu se pojaviti sledeći uslovi trke:
- Nit 1 čita vrednost promenljive 0.
- Nit 2 takođe čita vrednost promenljive 0.
- Nit 1 vrši sabiranje i upisuje rezultat 1 u promenljivu.
- Nit 2 vrši sabiranje i upisuje rezultat 1 u promenljivu, prebrisujeći rezultat niti 1.
Možete koristiti synchronized ključnu reč da zaključate ++ operaciju.
class Main {
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
synchronized (Main.class) {
count++;
}
}
};
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(task);
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("Konačni broj: " + count);
}
}Ili koristite AtomicInteger incrementAndGet() metod da zamenite ++ operaciju, garantujete atomičnost promenljive.
class Main {
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
count.incrementAndGet();
}
};
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(task);
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("Konačni broj: " + count.get());
}
}Scenarijo:Ima ključ kome je vrednost json struktura, u jsonu ima nekoliko podzadataka, ako ovi podzadaci izmene taj ključ, da li će postojati problem nit bezbednosti?
Će.
U okolini single-node, možete koristiti synchronized ključnu reč ili ReentrantLock da garantujete da je operacija izmene ključa atomična.
class KeyManager {
private final ReentrantLock lock = new ReentrantLock();
private String key = "{\"tasks\": [\"task1\", \"task2\"]}";
public String readKey() {
lock.lock();
try {
return key;
} finally {
lock.unlock();
}
}
public void updateKey(String newKey) {
lock.lock();
try {
this.key = newKey;
} finally {
lock.unlock();
}
}
}U okolini multi-node, možete koristiti distribuirano zaključavanje Redisson da garantujete da je operacija izmene ključa atomična.
class DistributedKeyManager {
private final RedissonClient redisson;
public DistributedKeyManager() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
this.redisson = Redisson.create(config);
}
public void updateKey(String key, String newValue) {
RLock lock = redisson.getLock(key);
lock.lock();
try {
// simulacija čitanja i ažuriranja operacija
String currentValue = readFromDatabase(key); // pretpostavimo čitanje JSON podataka
String updatedValue = modifyJson(currentValue, newValue); // izmena JSON
writeToDatabase(key, updatedValue); // pisanje nazad u bazu podataka
} finally {
lock.unlock();
}
}
private String readFromDatabase(String key) {
// simulacija čitanja iz baze podataka
return "{\"tasks\": [\"task1\", \"task2\"]}";
}
private String modifyJson(String json, String newValue) {
// korišćenje JSON biblioteke za analizu i izmenu
return json.replace("task1", newValue);
}
private void writeToDatabase(String key, String value) {
// simulacija pisanja nazad u bazu podataka
}
}Recite mi jedan scenario korišćenja nit bezbednosti?
Singleton (jedinačak) dizajn. U okolini više niti, ako više niti istovremeno pokuša da kreira instancu, singleton klasa mora garantovati da se kreira samo jedna instanca, i pružiti globalnu tačku pristupa.
Eager type (oštrouki tip) je relativno direktna metoda implementacije, garantuje nit bezbednost trenutnom inicijalizacijom singleton objekta pri učitavanju klase.
class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}Lazy type (lenji tip) singleton inicijalizira singleton objekat pri prvoj upotrebi, ovaj metod mora koristiti dvostruko proveravanje zaključavanja da bi se garantovala nit bezbednost, volatile ključna reč se koristi za garantovanje vidljivosti, synchronized ključna reč se koristi za garantovanje sinhronizacije.
class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // prva provera
synchronized (LazySingleton.class) {
if (instance == null) { // druga provera
instance = new LazySingleton();
}
}
}
return instance;
}
}Možete li reći podaci strukturi Hashtable?
Slično HashMap, podaci struktura Hashtable je takođe niz + lista, zatim kroz synchronized zaključavanje garantuje nit bezbednost.

- Java intervju vodič uključuje 360 intervju kandidat 3 Java pozadina tehnologija prvo lice intervju originalno pitanje: nit bezbednost, recite jedan scenario korišćenja - rekao sam singleton dvostruko proveravanje zaključavanja, lenji i oštri tip
- Java intervju vodič uključuje Kuaishou kandidat 2 prvo lice intervju originalno pitanje: ima int promenljiva 0, deset niti se redom izvršava ++ operaciju (ciklus 10000 puta), rezultat je veći ili manji ili jednak 100.000, zašto?
- Java intervju vodič uključuje TP Lianzhou kandidat 5 Java pozadina prvo lice originalno pitanje: kako garantovati nit bezbednost, Hashtable podaci struktura podložna
- Java intervju vodič uključuje Baidu kandidat 4 intervju originalno pitanje: šta znači nit bezbednost i nit nebezbednost?
- Java intervju vodič uključuje JD intervju kandidat 1 Java tehnologija prvo lice intervju originalno pitanje: osim ThreadLocal, koje metode rešavaju problem nit bezbednosti
[Zatvoreno zbog uklanjanja monetarizacije - QR kod baner]
memo: 30. januara 2025. izmenjeno do ovode.
ThreadLocal
Preporučeno čitanje: ThreadLocal kompletna analiza
12.🌟Šta je ThreadLocal?
ThreadLocal je alatka za realizaciju lokalnih promenljivih niti. Omogućava svakoj niti da ima svoju nezavisnu kopiju, time realizuje izolaciju niti.

Korišćenje ThreadLocal obično zahteva četiri koraka:
- Kreiranje ThreadLocal
//kreirajte ThreadLocal promenljivu
public static ThreadLocal<String> localVariable = new ThreadLocal<>();- Postavljanje vrednosti ThreadLocal
//postavite vrednost ThreadLocal promenljive
localVariable.set("Chenmo Wang Er je glupan");- Dobijanje vrednosti ThreadLocal
//dobijte vrednost ThreadLocal promenljive
String value = localVariable.get();- Brisanje vrednosti ThreadLocal
//obrišite vrednost ThreadLocal promenljive
localVariable.remove();U veb aplikacijama, možete koristiti ThreadLocal za čuvanje informacija o korisničkoj sesiji, tako da svaka nit može lako pristupiti informacijama o trenutnoj korisničkoj sesiji prilikom procesuiranja korisničkih zahteva.
U operacijama baze podataka, možete koristiti ThreadLocal za čuvanje objekata veze baze podataka, svaka nit ima svoju nezavisnu vezu baze podataka, time izbegavajući problem više niti koje se takmiče za istu vezu baze podataka.
public class DsContextHolder {
private static final ThreadLocal<DsNode> CONTEXT_HOLDER = new InheritableThreadLocal<>();
public static void reset() {
DsNode ds = CONTEXT_HOLDER.get();
if (ds == null) {
return;
}
if (ds.pre != null) {
CONTEXT_HOLDER.set(ds.pre);
} else {
CONTEXT_HOLDER.remove(); // obriši podatke u ThreadLocal
}
}
}U operacijama formatiranja, na primer formatiranje datuma, možete koristiti ThreadLocal za čuvanje SimpleDateFormat instanci, izbegavajući problem nit bezbednosti zbog deljenja istih instanci više niti.
Koje prednosti ima ThreadLocal?
Kopija promenljive kojoj pristupa svaka nit je nezavisna, izbegavajući problem nit bezbednosti uzrokovan deljenim promenljivama. Budući da ThreadLocal realizuje ekskluzivnost promenljivih niti, promenljive ne zahtevaju sinhrono procesuiranje, stoga može izbeći takmičenje resursa.
ThreadLocal se može koristiti za prenos kontekstualnih podataka preko metoda i klasa, bez potrebe za prenosom parametara između metoda.
- Java intervju vodič uključuje DiDi kandidat 2 tehnologija drugo lice originalno pitanje: koji problemi postoje kod ThreadLocal, zašto korišćenje nitnog bazena ima problem ponovne upotrebe
- Java intervju vodič uključuje Alipay intervju kandidat 2 prolećna regrutacija tehnologija prvo lice intervju originalno pitanje: recite mi o ThreadLocal? Ko referencira ThreadLocal?
- Java intervju vodič uključuje ByteDance intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: šta je ThreadLocal? Koji je princip implementacije ThreadLocal?
- Java intervju vodič uključuje Kuaishou intervju kandidat 1 odeljenje glavne stanice tehnologija intervju originalno pitanje: recite mi ulogu i scenario korišćenja ThreadLocal?
- Java intervju vodič uključuje Ctrip intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: ThreadLocal, (uloga, evolucija, meki pokazivač, proces brisanja)
- Java intervju vodič uključuje Meituan kandidat 9 prvo lice intervju originalno pitanje: koje prednosti ima threadlocal?
memo: 23. avgusta 2025. izmenjeno do ovode, danas je član sveta u VIP grupi postavio temu da je dobio ponudu za prenos na puno radno vreme u Meituan pozadini, zaista čestitati, krajem avgusta može preći na puno radno vreme, previše udobno.

13.Jeste li koristili ThreadLocal u radu?
Koristio sam, za čuvanje korisničkih informacija.

Tehnološka škola stvarni projekat je tipična MVC arhitektura, nakon prijave korisnik svaki put pristupa interfejsu, nosi token u zaglavlju zahteva, u sloju kontrole može analizirati osnovne informacije korisnika na osnovu ovog tokena.
Ako se u sloju servisa i sloju perzistencije takođe koriste korisničke informacije, možete u sloju kontrole presresti zahtev i čuvati korisničke informacije u ThreadLocal.

Na taj način možemo bilo gde izvući korisničke informacije čuvane u ThreadLocal.

Mnogi drugi scenariji cookie, sesija itd. podaci izolacije mogu se realizovati kroz ThreadLocal.

14.🌟Kako se implementira ThreadLocal?
Kada kreiramo ThreadLocal objekat i pozovemo set metod, u stvari inicijalizujemo ThreadLocalMap u trenutnoj niti.

ThreadLocalMap je statička unutrašnja klasa ThreadLocal, interno održava niz Entry, ključ je ThreadLocal objekat, vrednost je lokalna promenljiva niti, tako da za svaku nit održava kopiju promenljive.

Entry nasleđuje WeakReference, ograničava ključ na slabu referencu, prednost slabe reference je kada nedostaje memorije, JVM će vratiti ThreadLocal objekat, i postaviti odgovarajuću Entry.value na null, time može u velikoj meri izbeći curenje memorije.
static class Entry extends WeakReference<ThreadLocal<?>> {
/** Vrednost povezana sa ovim ThreadLocal. */
Object value;
//klasa čvora
Entry(ThreadLocal<?> k, Object v) {
//dodela ključa
super(k);
//dodela vrednosti
value = v;
}
}Skraćeni odgovor:
Princip implementacije ThreadLocal je da svaka nit održava Map, ključ je ThreadLocal objekat, vrednost je objekat za koji se želi realizovati izolacija niti.
- Kroz ThreadLocal set metod stavlja objekat u Map.
- Kroz ThreadLocal get metod uzima objekat iz Map.
- Veličina Map određuje broj ThreadLocal objekata.

memo: 18. septembra 2025. izmenjeno do ovode, danas je kandidat 27. generacije dobio svakodnevnu praksu ponudu od Xiaohongshu, posebno javio da se pohvalio, i pohvalio Ergovu intervju pobedu i Pai Congming projekat, čestitam!

Šta su slaba reference i jaka referenca?
Prvo recimo o jakoj referenci, na primer User user = new User("Chenmo Wang Er"), user je jaka referenca, new User("Chenmo Wang Er") je objekat jake reference.
Kada se user postavi na null (user = null), new User("Chenmo Wang Er") objekat će biti vraćen od sakupljača otpada; inače čak i ako nedostaje memorijskog prostora, JVM neće vratiti new User("Chenmo Wang Er") objekat jake reference, radije će baciti OutOfMemoryError.
Slaba referenca, na primer pri korišćenju ThreadLocal, ključ Entry je slab referencni objekat.
ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
userThreadLocal.set(new User("Chenmo Wang Er"));userThreadLocal je jaka referenca, new ThreadLocal<>() je objekat jake reference;
new User("Chenmo Wang Er") je objekat jake reference.
Nakon poziva set metoda, će staviti key = new ThreadLocal<>() u ThreadLocalMap, u ovom trenutku ključ je slab referencni objekat. Kada JVM vrši sakupljanje otpada, ako otkrije slab referencni objekat, gaće vratiti.

Lanac relacija je:
- ThreadLocal jaka referenca -> ThreadLocal objekat.
- Nit jaka referenca -> ThreadLocalMap.
ThreadLocalMap[i]jaka referenca -> Entry.- Entry.key slaba referenca -> ThreadLocal objekat.
- Entry.value jaka referenca -> lokalni promenljiv objekat niti.
- Java intervju vodič uključuje Alipay intervju kandidat 2 prolećna regrutacija tehnologija prvo lice intervju originalno pitanje: recite mi o ThreadLocal? Ko referencira ThreadLocal?
- Java intervju vodič uključuje ByteDance intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: šta je ThreadLocal? Koji je princip implementacije ThreadLocal?
- Java intervju vodič uključuje JD intervju kandidat 1 Java tehnologija prvo lice intervju originalno pitanje: princip ThreadLocal, koje probleme rešava
- Java intervju vodič uključuje Ctrip intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: ThreadLocal, (uloga, evolucija, meki pokazivač, proces brisanja)
- Java intervju vodič uključuje Shopee intervju kandidat 13 prvo lice intervju originalno pitanje: princip threadlocal, kako izbeći sakupljanje otpada?
memo: 23. avgusta 2025. izmenjeno do ovode. Danas je još jedan član sveta u VIP grupi javio da je dobio ponudu za prenos na puno radno vreme u Meituan, i izjavio: letnja praksa se zaista može klati grupnom, stopa prenosa na puno radno vreme doseže 100%.

15.🌆Kako se dešava curenje memorije ThreadLocal?
ThreadLocalMap ključ je slab referenca, ali vrednost je jaka referenca.
Ako nit stalno radi, i vrednost stalno pokazuje na određeni objekat jake reference, onda taj objekat neće biti vraćen, time uzrokujući curenje memorije.

Kako rešiti problem curenja memorije?
Vrlo jednostavno, nakon korišćenja ThreadLocal, pravovremeno pozovite remove() metod da oslobodite memorijski prostor.
try {
threadLocal.set(value);
// izvršavanje poslovnih operacija
} finally {
threadLocal.remove(); // osigurajte da će se izvršiti čišćenje
}remove() će pozvati remove metod ThreadLocalMap da protraga heš tabelu, pronađje Entry gde je ključ jednak trenutnom ThreadLocal, nakon pronalaska će pozvati clear metod Entry, postaviti vrednost Entry na null.
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
// izračunaj hash vrednost ključa
int i = key.threadLocalHashCode & (len-1);
// proteri niz, pronađi Entry gde je ključ null
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
// postavi ključ tog Entry na null (to jest Entry ne važi)
e.clear();
// očisti zastareli entry
expungeStaleEntry(i);
return;
}
}
}
public void clear() {
this.referent = null;
}Zatim izvrši expungeStaleEntry() metod, čisti Entry gde je ključ null.

Da li svaki put remove nakon operacije?
Ne svaki put remove, uglavnom na osnovu scenario korišćenja odlučujem. U nekim scenima kratkog životnog veka, poput procesuiranja informacija o kontekstu pojedinačnog HTTP zahteva, obično ću ujedinjeno remove na kraju zahteva.
public class ReqInfoContext {
private static TransmittableThreadLocal<ReqInfo> contexts = new TransmittableThreadLocal<>();
public static void addReqInfo(ReqInfo reqInfo) {
contexts.set(reqInfo);
}
public static void clear() {
contexts.remove(); // obriši podatke u ThreadLocal
}
public static ReqInfo getReqInfo() {
return contexts.get();
}
}Ali u scenima gde treba održati stanje kroz više metoda, neće svaki put remove.
Moj princip korišćenja je:
- Pri korišćenju na nivou metoda, try-finally garantuje remove
- Pri korišćenju na nivou zahteva, putem interceptor-a ili Filter-a ujedinjeno čišćenje
- Ako sačuvani objekat je veliki, odmah nakon korišćenja remove
- Redovno proveravajte korišćenje ThreadLocal, izbjegavajte zaborav
Zašto je ključ dizajniran kao slab referenca?
Prednost slabe reference je što kada nedostaje memorije, JVM može pravovremeno vratiti objekat slabe reference.
Na primer:
WeakReference key = new WeakReference(new ThreadLocal());key je slaba referenca, new WeakReference(new ThreadLocal()) je objekat slabe reference, kada JVM vrši sakupljanje otpada, samo otkrije slab referencni objekat, će ga vratiti.
Jednom kada se ključ vrati, ThreadLocalMap prilikom set, get će očistiti Entry gde je ključ null.

Sažeto, nakon što se ThreadLocal vrati od sakupljača otpada, sledeći pristup ThreadLocalMap, Java će automatski očistiti one entry-je gde je ključ null, ovaj proces seće se prilikom get(), set(), remove().

Koja poboljšanja ThreadLocal poznajete?
U JDK 20 Early-Access Build 28 verziji, pojavilo se poboljšanje ThreadLocal, to jest ScopedValue.
I FastThreadLocal u Netty-ju, to je Netty optimizacija ThreadLocal, interno održava indeksnu konstantu index, svaki put kada se kreira FastThreadLocal će automatski +1, zamjenjuje gubitak uzrokovan hash sukobom, prostor zamjenjuje vreme.
private final int index;
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index < 0) {
nextIndex.decrementAndGet();
}
return index;
}Kao i Aliev TransmittableThreadLocal, ne samo realizuje funkciju da potomske niti mogu naslediti parent ThreadLocal, već može prenositi vrednosti kroz nitni bazen.
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
// postavljanje u parent niti
context.set("vrednost-postavljena-u-parent");
// može se čitati u potomskoj niti, vrednost je "vrednost-postavljena-u-parent"
String value = context.get();
- Java intervju vodič uključuje DiDi kandidat 2 tehnologija drugo lice originalno pitanje: koji problemi postoje kod ThreadLocal, zašto korišćenje nitnog bazena ima problem ponovne upotrebe
- Java intervju vodič uključuje Tencent intervju kandidat 22 letnja praksak prvo lice intervju originalno pitanje: u kom slučaju ThreadLocal izaziva curenje memorije
- Java intervju vodič uključuje ByteDance intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: ima li problema pri korišćenju ThreadLocal? Kako rešiti?
- Java intervju vodič uključuje Kuaishou intervju kandidat 1 odeljenje glavne stanice tehnologija intervju originalno pitanje: koji nedostaci ima ThreadLocal? Koja poboljšanja ThreadLocal poznajete?
- Java intervju vodič uključuje Ctrip intervju kandidat 1 Java pozadina tehnologija prvo lice intervju originalno pitanje: ThreadLocal, (uloga, evolucija, meki pokazivač, proces brisanja)
- Java intervju vodič uključuje Meituan kandidat 9 prvo lice intervju originalno pitanje: koji problemi će se pojaviti kod threadlocal? Kako rešiti curenje memorije?
memo: 23. avgusta 2025. izmenjeno do ovode. Danas dok sam pomagao članu sveta da promeni životopis, dobio sam takvu povratnu informaciju: pridružio se svetu u januaru, u martu dobio jednu malu svakodnevnu praksu, kasnije dobio letnju praksu u Ant Group, sreda je odbrana, prenos na puno radno vreme nema problema. Zaista zahvalan na pozitivnoj povratnoj informaciji članova sveta, to je moja najjača motivacija za nastavak.

16.Jeste li čitali izvorni kod ThreadLocalMap?
Istraživao sam.
Iako se ThreadLocalMap zove Map, ne implementira Map interfejs, to je jednostavna linearna probna heš tabela.
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k); // Ovaj ključ je WeakReference
value = v;
}
}
private Entry[] table; // niz koji čuva ThreadLocal promenljive
private int size; // trenutni broj Entry
private int threshold; // prag za pokretanje proširenja
}Podložna struktura podataka je takođe niz, svaki element u nizu je Entry objekat, Entry objekat nasleđuje WeakReference, ključ je ThreadLocal objekat, vrednost je lokalna promenljiva niti.

Kada se pozove ThreadLocal.set(value), staviće vrednost u ThreadLocalMap.
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}set() metod je ključni metod ThreadLocalMap, kroz heš kod ključa i dužinu niza modulira, izračunava poziciju ključa u nizu, ova tačka je slična implementaciji HashMap.
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len - 1); // izračunaj indeks
for (Entry e = tab[i]; e != null; e = tab[nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) { // ako ključ već postoji, ažuriraj vrednost
e.value = value;
return;
}
if (k == null) { // Ključ je null, očisti nevažeći Entry
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value); // direktno ubaci Entry
size++;
if (size >= threshold) {
rehash();
}
}Izračun threadLocalHashCode ima nešto, svaki put kada se kreira ThreadLocal objekat, dodaće zlatni presek, može heš kod rasporediti vrlo ravnomerno.
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}Kada se pozove ThreadLocal.get(), pozivaće getEntry metod ThreadLocalMap, na osnovu heš koda ključa pronađe odgovarajuću lokalnu promenljivu niti.
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key) { // ako ključ postoji, direktno vrati
return e;
} else {
return getEntryAfterMiss(key, i, e); // nastavi tražiti
}
}Kada se pozove ThreadLocal.remove(), pozivaće remove metod ThreadLocalMap, na osnovu heš koda ključa pronađe odgovarajuću lokalnu promenljivu niti, očisti je, sprečava curenje memorije.
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len - 1);
for (Entry e = tab[i]; e != null; e = tab[nextIndex(i, len)]) {
if (e.get() == key) {
e.clear(); // očisti WeakReference
e.value = null; // oslobodi Value
expungeStaleEntries();
return;
}
}
}17.Kako ThreadLocalMap rešava hash sukob?
Metoda otvorenog adresiranja.
Ako je izračunati slot i već zauzet, ThreadLocalMap će koristiti linearnu probu metode otvorenog adresiranja za pronalaženje sledećeg slobodnog slota:
Ako je pozicija i zauzeta, pokušaj i+1.
Ako je i+1 takođe zauzeto, nastavi probati i+2, dok ne pronađeš prazno mesto.
Ako dođeš do kraja niza, vrati se na početak niza, nastavi tražiti prazno mesto.
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}Zašto koristiti metodu linearne probe umesto metode lanca HashMap za rešavanje hash sukoba?
Cilj dizajna ThreadLocalMap je čuvanje podataka privatnih niti, neće biti puno ključeva, stoga je korišćenje linearne probre prostornije efikasnije.
Metoda lanca još treba posebno održavati listu, pa čak i crveno-crno stablo, ne odgovara scenama ThreadLocal.
Znate li metodu otvorenog adresiranja?
Jednostavno rečeno, ako je taj rupe zauzeta, onda idi da nađeš praznu rupu.

Ako ubacimo podatke gde je value=27, kroz hash izračun treba pasti u 4. slot, a 4. slot već ima podatke, i ključ se ne poklapa sa trenutnim.
U ovom trenutku će linearno tražiti unazad, sve dok ne pronađe Entry gde je null, tek će stati.
memo: 3. februara 2025. izmenjeno do ovode.
18.Znate li mehanizam proširenja ThreadLocalMap?
Znam.
Različito od HashMap, ThreadLocalMap neće direktno proširiti kada broj elemenata dosegne prag, već će prvo očistiti ključeve vraćene od GC, a zatim proširiti kada se stopa popunjenosti dostigne do tri četvrtine.
private void rehash() {
// očisti ključeve vraćene od GC
expungeStaleEntries();
//proširenje
if (size >= threshold - threshold / 4)
resize();
}Proces čišćenja će proteri kroz ceo niz, očistiti Entry gde je ključ null.
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
Entry e = tab[j];
// ako je ključ null, očisti Entry
if (e != null && e.get() == null)
expungeStaleEntry(j);
}
}Podrazumevana vrednost praga threshold je dve trećine dužine niza.
private void setThreshold(int len) {
threshold = len * 2 / 3;
}Pri proširenju, će duplirati dužinu niza, a zatim ponovo izračunati poziciju svakog Entry, koristeći metodu linearne probe za pronalaženje novog praznog mesta, zatim staviti Entry u novi niz.
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
// proširi na duplo veće
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
// proteri kroz stari niz
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // oslobodi Value, spreči curenje memorije
} else {
// ponovo izračunaj poziciju
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null) {
// linearna probe traži novu poziciju
h = nextIndex(h, newLen);
}
// stavi u novi niz
newTab[h] = e;
count++;
}
}
}
table = newTab;
size = count;
threshold = newLen * 2 / 3; // ponovo izračunaj prag proširenja
}Jednom rečenicom sažeto: ThreadLocalMap koristi strategiju "prvo očistiti zatim proširiti", pri proširenju se dužina niza duplira, ponovo se izračunava indeks, ako se desi hash sukob, koristi metodu linearne probe za rešavanje.

19.Može li roditeljska nit prenositi vrednosti potomskim nitima kroz ThreadLocal?
Ne.

Budući da ThreadLocal promenljive čuvaju u ThreadLocalMap svake niti, a potomske niti ne nasleđuju ThreadLocalMap roditeljske niti.
Možete koristiti InheritableThreadLocalda rešite ovaj problem.

Potomska nit će kopirati InheritableThreadLocal promenljive roditeljske niti pri kreiranju.

Pogledajmo primer korišćenja:
class InheritableThreadLocalExample {
private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
inheritableThreadLocal.set("vrednost roditeljske niti");
new Thread(() -> {
System.out.println("vrednost koju potomska nit dobija: " + inheritableThreadLocal.get()); // nasledila vrednost roditeljske niti
}).start();
}
}Znate li princip InheritableThreadLocal?
Znam.
U definiciji Thread klase, svaka nit ima dva ThreadLocalMap:
public class Thread {
/* Obična ThreadLocal promenljiva gde se čuvaju podaci */
ThreadLocal.ThreadLocalMap threadLocals = null;
/* InheritableThreadLocal promenljiva gde se čuvaju podaci */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}Obična ThreadLocal promenljiva se čuva u threadLocals i ne nasleđuje se u podnitkovima.
InheritableThreadLocal promenljiva se čuva u inheritableThreadLocals, kada se new Thread() kreira podnit, Thread-ina init() metoda proverava da li roditeljska nit ima inheritableThreadLocals, ako ima, kopira InheritableThreadLocal promenljivu u podnit:
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
// Dobavlja trenutnu roditeljsku nit
Thread parent = currentThread();
// Kopira InheritableThreadLocal promenljivu
if (parent.inheritableThreadLocals != null) {
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
}memo: 4. februar 2025. izmenjeno do ovde.
Java memorijski model
20.🌟Kako razumeteš Java memorijski model?
Preporučeno čitanje: Pričajmo o Java memorijskom modelu
Java memorijski model je apstraktni model definisan u JVM specifikaciji, koji se koristi za opisivanje vidljivosti deljenih promenljivih u višenitnoj okolini.

Deljene promenljive se čuvaju u glavnoj memoriji, svaka nit ima privatnu lokalnu memoriju koja čuva kopije deljenih promenljivih.
- Kada nit izmeni kopiju deljene promenljive u lokalnoj memoriji, potrebno je da JVM osveži podatke u glavnoj memoriji da bi druge niti mogle da vide te izmene.
- Kada nit treba da pročita deljenu promenljivu, obično će čitati iz lokalne memorije. Ako je kopija u lokalnoj memoriji zastarela, JVM će osvežiti najnoviju vrednost deljene promenljive iz glavne memorije u lokalnu memoriju.

Zašto nit koristi sopstvenu memoriju?
Nit kopira promenljive iz glavne memorije u radnu memoriju, čime se smanjuje trošak CPU pristupa RAM-u.
Svaka nit ima sopstvenu kopiju promenljivih, čime se izbjegava konflikt podataka uzrokovan istovremenom izmnom deljenih promenljivih od strane više niti.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje FanSoft studenta 3 za Java backend prvi intervju: Zašto nit koristi sopstvenu memoriju
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje BYD studenta 3 za Java tehnički prvi intervju: Pričaj o JMM
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Qunar studenta 1 za tehnički drugi intervju: Pričaj o JMM modelu
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Meituan studenta 15 za backend tehnički prvi intervju: jmm memorijski model šta se čuva na steku i u metodi
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Kuaishou studenta 1 za odsjek glavne stanice tehnički dio: Pričaj o JVM memorijskom modelu?
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Tencent studenta 29 za Java backend prvi intervju: Pričaj o JVM memorijskom modelu?
memo: 5. februar 2025. izmenjeno do ovde.
21. Da li je i++ atomična operacija?
Ne, sastoji se od tri koraka:
- Čita vrednost i iz memorije.
- Dodaje 1 na i.
- Upisuje novu vrednost nazad u memoriju.
Kako razumiješ atomičnost, vidljivost i uređenost?
Atomičnost zahteva da operacija bude nedeljiva, ili se izvrši u potpunosti ili se uopšte ne izvrši.
Na primer: count++ nije atomična operacija, obuhvata čitanje count vrednosti, dodavanje 1, upisivanje count - tri koraka, stoga je potrebno zaključavanje ili korišćenje AtomicInteger umesto int da bi se osigurala atomičnost.
Vidljivost zahteva da izmene deljene promenljive od strane jedne niti mogu biti pravovremeno vidljive drugim nitima.
Objasniću kroz sledeći kod:
private static boolean flag = true;
public static void main(String[] args) {
new Thread(() -> {
while (flag) {} // Nit A možda neće videti flag=false
System.out.println("Nit A izašla");
}).start();
try { Thread.sleep(1000); } catch (InterruptedException e) {}
flag = false; // Nit B menja flag
}Nit A će keširati flag=true u lokalnoj memoriji, iako je nit B izmenila flag=false, to se neće odmah sinhronizovati u glavnu memoriju i lokalnu memoriju niti A, stoga će nit A ostati u beskonačnoj petlji.
Rešenje je korišćenje volatile ključne reči za osiguravanje vidljivosti.
Uređenost se odnosi na to da li se redosled izvršenja programa odvija prema redosledu pisanja koda.
U jednonitnoj okolini, kod se može tačno izvršavati prema napisanom redosledu. Ali u višenitnoj okolini, CPU i kompajler mogu vršiti preuređivanje instrukcija, tako da se redosled izvršenja koda može promeniti.
Objasniću kroz sledeći kod:
int a = 0, b = 0;
boolean flag = false;
void thread1() {
a = 1;
flag = true; // Može se optimisati od strane CPU, izvršiti se prvo
}
void thread2() {
if (flag) {
System.out.println(a); // Može isprintati 0, a ne 1
}
}Zbog preuređivanja instrukcija, flag = true se može izvršiti pre a = 1, što dovodi do toga da thread2() čita flag=true, ali a je i dalje 0, što dovodi do situacije koja ne odgovara logici koda.
Kratak odgovor:
Atomičnost osigurava da operacija nije prekidiva, vidljivost osigurava da niti vide najnoviju vrednost posle izmene promenljive, uređenost osigurava konzistentan redosled izvršenja koda, ove karakteristike se mogu osigurati kroz volatile, synchronized i CAS mehanizme.
Da li je sledeći kod atomična operacija?
int i = 2;
int j = i;
i++;
i = i + 1;- Prva linija koda je dodela osnovnog tipa, to je atomična operacija.
- Druga linija prvo čita vrednost i, zatim dodeljuje j, to nije atomična operacija.
- Treća i četvrta linija nisu atomične operacije, obe prvo moraju pročitati vrednost i, zatim +1, pa zatim dodeliti i.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje JD studenta 4 za cloud practice intervju: i++ je li atomična operacija
22. Pričaj o preuređivanju instrukcija?
Preuređivanje instrukcija je tehnika optimizacije kojom CPU ili kompajler menjaju redosled izvršenja koda kako bi poboljšali efikasnost izvršenja programa.
Od Java izvornog koda do konačnog niza instrukcija koje se izvršavaju, prolazi se kroz 3 vrste preuređivanja: preuređivanje na nivou kompajlera, preuređivanje paralelnih instrukcija, preuređivanje na nivou memorijskog sistema.

Preuređivanje instrukcija može dovesti do neuspjeha dvostruke provere kod zaključavanja, na primer u sledećem kodu Singleton paterna:
public class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // Prva provera
synchronized (Singleton.class) {
if (instance == null) { // Druga provera
instance = new Singleton(); // Može doći do preuređivanja instrukcija
}
}
}
return instance;
}
}Ako nit A izvrši instance = new Singleton();, ali konstruktor još uvek nije završen, nit B može pročitati neinicijalizovani objekat, što dovodi do null pointer izuzetka.

Pravi način je dodati volatile ključnu reč na instance promenljivu, čime se zabranjuje preuređivanje instrukcija.
class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // Zbog volatile, zabranjeno preuređivanje instrukcija
}
}
}
return instance;
}
}memo: 6. februar 2025. izmenjeno do ovde.
23. Jeste li upoznati sa happens-before?
Happens-Before je pravilo definisano od strane Java memorijskog modela koje osigurava vidljivost i uređenost između niti.
Ako operacija A Happens-Before operacija B, onda:
- Rezultat operacije A je vidljiv operaciji B.
- Operacija A se vremenski izvršava pre operacije B.
Drugim rečima, ako A Happens-Before B, onda A-ine izmene moraju biti vidljive B, i B ne može biti preuređena pre A.
Koja Happens-Before pravila poznajete?

JMM propisuje 6 Happens-Before pravila, operacije koje zadovoljavaju ova pravila neće biti preuređene i osigurava se vidljivost podataka.
① Pravilo redosleda programa: U jednoj niti, kod se izvršava po redu; na primer a = 1; b = 2;, a se izvršava pre b.
② Pravilo zaključavanja monitora: unlock() Happens-Before lock(); na primer nakon što synchronized otpusti bravu, nit koja zaključa može videti najnovije podatke.
③ Pravilo volatile promenljivih: Pisanje volatile promenljive Happens-Before čitanje volatile.
④ Pravilo tranzitivnosti: A Happens-Before B i B Happens-Before C, onda A Happens-Before C. Na primer a = 1 pre b = 2, b = 2 pre c = 3, onda a = 1 pre c = 3.
⑤ Pravilo pokretanja niti: Nit A izvrši operaciju ThreadB.start(), onda A-ina operacija ThreadB.start() happens-before bilo kojoj operaciji u nit B.
⑥ Pravilo završetka niti: Sve operacije niti Happens-Before Thread.join(); na primer nakon t.join();, glavna nit sigurno može videti t-ine izmene.
memo: 7. februar 2025. izmenjeno do ovde.
24. Jeste li upoznati sa as-if-serial?
As-If-Serial pravilo dozvolja CPU-u i kompajleru da optimizuju redosled koda, ali ne menja rezultat izvršenja u jednoj niti. Primjenjuje se samo na jednu nit, u višenitnoj okolini se i dalje može desiti preuređivanje instrukcija, potrebne su volatile i synchronized mehanizmi za osiguravanje uređenosti.
Objasniću ovaj primer.
double pi = 3.14; // A
double r = 1.0; // B
double area = pi * r * r; // CC zavisi od A, istovremeno C zavisi i od B.

Stoga u konačnom nizu instrukcija koje se izvršavaju, C ne može biti preuređena ispred A ili B, inače bi došlo do greške.
Ali između A i B nema zavisnosti, stoga kompajler i procesor mogu preurediti redosled izvršenja A i B.
Stoga program može imati dva redosleda izvršenja:

Happens-Before pravilo osigurava uređenost u višenitnoj okolini, sprečava konkurentne probleme uzrokovane preuređivanjem instrukcija. As-If-Serial pravilo osigurava da jednonitni kod neće pogrešiti usled optimizacije.
25.🌟 Jeste li upoznati sa volatile?
Preporučeno čitanje: Analiza volatile ključne reči
Upoznat/a.
Prvo, osigurava vidljivost, nakon što nit izmeni volatile promenljivu, druge niti mogu odmah videti najnoviju vrednost; drugo, sprečava preuređivanje instrukcija, upisivanje volatile promenljive neće biti preuređeno pre koda koji je prethodi.
Kako volatile osigurava vidljivost?
Kada nit vrši operaciju pisanja volatile promenljive, JVM će nakon upisa ove promenljive ubaciti instrukciju memorijske barijere, ova instrukcija će prisiliti osvežavanje vrednosti promenljive iz lokalne memorije u glavnu memoriju.

StoreStore; // Osigurava da se prethodne operacije ne preuređuju
volatile_write(); // Upis volatile promenljive
StoreLoad; // Osigurava da je posle upisa vidljivo drugim nitima---ovaj deo se na intervjuu ne mora napamet naučiti start--- Na x86 arhitekturi, obično se koristi lock instrukcija za realizaciju memorijske barijere prilikom upisa, na primer:
mov [a], 2 ; Upisuje vrednost 2 na memorijsku adresu a
lock add [a], 0 ; lock instrukcija služi kao memorijska barijera, osigurava memorijsku vidljivost---ovaj deo se na intervjuu ne mora napamet naučiti end---
Kada nit vrši operaciju čitanja volatile promenljive, JVM će ubaciti instrukciju memorijske barijere, ova instrukcija će prisiliti da vrednost promenljive u lokalnoj memoriji postane nevažeća, čime se ponovo čita najnovija vrednost iz glavne memorije.

Kada se deklariše volatile promenljiva x:
volatile int x = 0Nit A nakon upisa x će osvežiti njenu najnoviju vrednost u glavnu memoriju, nit B pri čitanju x će zato što je x u njenoj lokalnoj memoriji nevažeći, čitati iz glavne memorije najnoviju vrednost.

Kako volatile osigurava uređenost?
JVM će pre i posle čitanja/pisanja volatile promenljive ubaciti "memorijsku barijeru" kako bi se ograničilo ponašanje CPU-a i kompajlera pri optimizaciji:
- StoreStore barijera može zabraniti preuređivanje običnog upisa i volatile upisa
- StoreLoad barijera zabranjuje preuređivanje volatile upisa i volatile čitanja
- LoadLoad barijera zabranjuje preuređivanje volatile čitanja i narednog običnog čitanja
- LoadStore barijera zabranjuje preuređivanje volatile čitanja i narednog običnog upisa
Da li u jednoj niti postoji velika razlika u troškovima između dodavanja i nedodavanja volatile?
U jednoj niti, volatile troškovi su relativno mali, jer nema konkurentne borbe niti i troškova promene konteksta.
Ali volatile ipak uvodi neke dodatne memorijske barijere, kako bi se osigurala memorijska vidljivost. Ranije sam testirao/la, 100 miliona operacija nad običnom promenljivom trajalo je 2-3 milisekunde, nad volatile promenljivom 100 miliona operacija trajalo je 25-29 milisekundi, otprilike 10 puta više nego obična promenljiva.

Iako je relativna razlika velika, apsolutna vremenska razlika je mala, sve je na nivou milisekundi.
Volatile forsira pisanje i čitanje glavne memorije, da li to ne utiče na performanse, mnoge Java alatke kao AQS koriste volatile, kako rešavaju ovaj problem performansi
Volatile zaista donosi određene troškove, uključujući:
- Zabranjuje optimizaciju CPU keša, svaki put se mora sinhronizovati sa glavnom memorijom
- Ubacivanje memorijskih barijera, sprečavanje preuređivanja instrukcija
- Na određenim arhitekturama, dovodi do nevaženja redova keša CPU-a
Ali! Moderni CPU i JVM su napravili mnogo optimizacija, volatile troškovi su svedeni na prihvatljiv nivo.
Prvo, moderni CPU imaju višenivojski keš (L1, L2, L3), volatile promenljive iako se ne mogu keširati u registrima, i dalje mogu koristiti CPU keš.

Samo je potrebno kroz protokol konzistencije keša (MESI) osigurati vidljivost.

// Na primer u x86 arhitekturi
volatile int state = 0;
// operacija čitanja: čita iz L1/L2/L3 keša, dok je red keša najnoviji
// operacija upisa: upis u keš, istovremeno putem MESI protokola obaveštava druge CPU keševe da postanu nevažećimDrugo, JVM će birati optimalnu memorijsku barijeru prema različitim CPU arhitekturama:
// U x86 arhitekturi, skup volatile upisa je otprilike:
// mov [memorijska adresa], registar // Običan upis
// lock addl $0, (%rsp) // Memorijska barijera, ali u x86 jakom memorijskom modelu trošak je mali
// U ARM arhitekturi potrebno je više barijera:
// str registar, [memorijska adresa] // Upis
// dmb sy // Memorijska barijera podatakaAQS dizajn je veoma suptilan, volatile koristi samo na apsolutno neophodnim mestima. Na primer state mora biti volatile, jer sve niti moraju videti najnoviju vrednost, ali nextWaiter u Node-u ne treba, jer se pristupa samo dok se drži brava.
public abstract class AbstractQueuedSynchronizer {
// Samo je state volatile
private volatile int state;
// Većina polja u čvorovima reda nije volatile
static final class Node {
volatile Node prev; // Samo ono što zahteva vidljivost je volatile
volatile Node next;
volatile Thread thread;
Node nextWaiter; // Ono što ne zahteva jaku konzistenciju nije volatile
}
}AQS ekstenzivno koristi Unsafe klasu za finiju kontrolu:
// Obično volatile čitanje i pisanje
volatile int state;
int s = state; // volatile čitanje
state = s + 1; // volatile pisanje
// AQS koristi Unsafe
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
// Može se selektivno koristiti različiti nivo memorijske semantike
unsafe.getInt(this, stateOffset); // Obično čitanje
unsafe.getIntVolatile(this, stateOffset); // volatile čitanje
unsafe.compareAndSwapInt(this, stateOffset, expect, update); // CASKaka je razlika između volatile i synchronized?
volatile ključna reč se koristi za modifikovanje promenljivih, osigurava da su operacije ažuriranja te promenljive vidljive svim nitima, tj. jednom kad neka nit izmeni volatile promenljivu, druge niti će odmah videti najnoviju vrednost.
synchronized ključna reč se koristi za modifikovanje metoda ili blokova koda, osigurava da u istom trenutku samo jedna nit može izvršiti taj metod ili blok koda, čime se realizuje ekstluitelni pristup.
Kaka je razlika kada se volatile stavi na osnovni tip i na objekat?
Kada se volatile koristi za osnovne podatkovne tipove, osigurava da se operacije čitanja i pisanja te promenljive vrše direktno iz glavne memorije ili u nju.
private volatile int count = 0;Kada se volatile koristi za referentni tip, osigurava vidljivost samog referenciranja, tj. osigurava da adresa objekta na koju ukazuje referenca bude najnovija.
Ali, volatile ne osigurava thread-safety unutrašnjeg stanja referentnog objekta.
private volatile SomeObject obj = new SomeObject();Iako volatile osigurava vidljivost obj reference, new SomeObject() objekat na koji obj ukazuje nije zaštićen volatile-om.
Ako je potrebno osigurati thread-safety unutrašnjeg stanja referentnog objekta, potrebno je koristiti synchronized ili ReentrantLock i slične mehanizme zaključavanja.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Tencent Cloud pametni studenta 16 za prvi intervju: Ručno pisanje Singleton paterna, pominju se synchronized i volatile, pokvaćeno pitaju i o realizaciji ova dva principa
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Ctrip studenta 1 za Java backend tehnički prvi intervju: Kako volatile osigurava vidljivost (cpu keš i glavni keš)
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje 360 studenta 3 za Java backend tehnički prvi intervju: volatile ključna reč, pričaj o drugim poznatim ključnim rečima
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Kuaishou studenta 5 za intervju: razlika između synchronized i volatile
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Xiaomi studenta F za intervju: Šta volatile osigurava (pitanje o specifičnim memorijskim barijerama), razlika kada se volatile stavi na osnovni tip i na objekat
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Meituan studenta 15 Dianping backend tehnički intervju: Pitanje o volatile, pričao o JMM i kako volatile realizuje uređenost i vidljivost
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Li Auto studenta 2 za prvi intervju: Da li ste upoznati sa volatile? Dodatno pitanje: Kako se realizuje na nivou asembler jezika?
memo: 20. septembar 2025. izmenjeno do ovde, danas jedan član kluba je tokom konsultacije pominjao da je uspešno prešao na stalno zaposlenje u Tencent-u, čestitam mu, zaista je jako jak. Tencent se može reći najbolja internet kompanija u Kini, bez ikakvog izuzetka.

Brave
26. Jeste li koristili synchronized?
Koristio/la, vrlo često.
synchronized nakon JDK 1.6 je prošao optimizaciju brave, dodao je pristrasnu bravu, laku bravu, što je značajno poboljšalo synchronized performanse.
Šta se zaključava kada se koristi synchronized?
Kada se synchronized koristi na običnom metodu, zaključava se objekat koji izvršava taj metod.
public synchronized void increment() {
this.count++;
}Kada se synchronized koristi na statičkom metodu, zaključava se Class objekat te klase.
public static synchronized void increment() {
count++;
}Kada se synchronized koristi na bloku koda, zaključava se objekat naveden u zagradi, na primer trenutni objekat this.
public void increment() {
synchronized (this) {
this.count++;
}
}
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje 360 studenta 3 za Java backend tehnički prvi intervju: Pričaj o drugim poznatim ključnim rečima, na primer synchronized
27. Jeste li upoznati/a sa principom realizacije synchronized?
synchronized zavisi od JVM-ovog internog Monitor objekta za realizaciju sinhronizacije niti. Pri korišćenju nije potrebno ručno lock i unlock, JVM automatski zaključava i otključava.
Kada synchronized zaključa blok koda, JVM kroz monitorenter, monitorexit dve instrukcije realizuje sinhronizaciju:
- Prva predstavlja da nit pokušava da dobije Monitor objekat lock;
- Druga predstavlja da nit je završila izvršenje sinhronizovanog bloka koda i oslobađa bravu.
Kada se koristi javap -c -s -v -l SynchronizedDemo.class za dekompajliranje synchronized bloka koda, mogu se videti ove dve instrukcije.

Kada synchronized modifikuje običan metod, JVM kroz ACC_SYNCHRONIZED marker realizuje sinhronizaciju.

Koliko poznajete Monitor?
Monitor je JVM-ov ugrađeni mehanizam sinhronizacije, svaki objekat u memoriji ima zaglavlje objekta — Mark Word, koje se koristi za čuvanje stanja brave, kao pokazivač na Monitor objekat.

synchronized zavisi od Mark Word u zaglavlju objekta za upravljanje stanjem, podržava bez brave, pristrasnu bravu, laku bravu i tešku bravu.
U Hotspot virtuelnoj mašini, Monitor realizuje ObjectMonitor:
ObjectMonitor() {
_count = 0; // Bilježi koliko puta je nit dobila bravu
_owner = NULL; // Ukazuje na nit koja drži ObjectMonitor
_WaitSet = NULL; // Nit u wait stanju, dodaje se u _WaitSet
_cxq = NULL ;
_EntryList = NULL ; // Nit u stanju blokiranja čekanja na bravu, dodaje se u ovu listu
}- _owner: Trenutna nit koja drži ObjectMonitor, početna vrednost je null, označava da nijedna nit ne drži bravu. Nakon što nit uspešno dobije bravu, ova vrednost se ažurira na ID niti, nakon puštanja brave se resetuje na null.
- _count: Bilježi koliko puta trenutna nit dobija bravu (reentrant brava), svaki put nakon uspešnog zaključavanja
_count + 1, puštanje brave_count - 1. - _WaitSet: Red čekanja, nakon poziva metoda
wait(), nit će osloboditi bravu i pridružiti se _WaitSet, ući u WAITING stanje, čekajućinotify()da je probudi. - _cxq: Blokirani red, koristi se za čuvanje niti koje su tek ušle u Monitor (još nisu ušle u _EntryList).
- _EntryList: Red konkurencije, sve niti koje čekaju da dobiju bravu (BLOCKED stanje) ulaze u _EntryList, čekajući da se brava oslobodi da bi se takmičile za pravo izvršenja.
Strukturna shema:
+----------------------+
| ObjectMonitor |
| ---------------- |
| _owner = Thread-1 | // Trenutna nit koja drži bravu
| _count = 1 | // Koliko puta je nit dobila bravu
| _WaitSet -> T3,T4 | // Nit koja je izvršila wait()
| _EntryList -> T2,T5| // Nit koja se takmiči za bravu
| _cxq -> T6,T7 | // Novo ušle niti
+----------------------+Za li se dodiruje OS nivo?
Da, kada se synchronized nadogradi na tešku bravu, zavisi od operativnog sistema mutex mehanizma za realizaciju, mutex se koristi da osigura da u bilo kom datom trenutku samo jedna nit može izvršiti određeni segment koda.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Qunar studenta 1 za tehnički drugi intervju: synchronized osnovni princip, da li se dodiruje OS nivo
memo: 24. septembar 2025. izmenjeno do ovde. Danas jedan član kluba u planetu je poslao konsultaciju da je dobio ponudu za redovnu praksu u Meituan-u, pita da li treba i dalje da traži drugu praksu, ovde prvo moramo da čestitamo, sledeće godine u martu/aprilu može direktno na letnju praksu.

28. Kako synchronized osigurava vidljivost?
Kroz dva koraka:
- Pri zaključavanju, nit mora pročitati najnovije podatke iz glavne memorije.
- Pri puštanju brave, nit mora izmenjene podatke upisati nazad u glavnu memoriju, tako da druge niti nakon dobijanja brave mogu videti najnovije podatke.
Nit A Nit B
┌────────────────────┐
│ synchronized(lock) │
│ x = 1; │ // 1. Nit A menja promenljivu x
└────────────────────┘
↓ Oslobađa bravanu
(JVM forsira osvežavanje x u glavnu memoriju)
(Nit B dobija bravanu)
┌────────────────────┐
│ synchronized(lock) │
│ print(x); │ // 2. Nit B čita najnovije x=1
└────────────────────┘Kako synchronized osigurava uređenost?
synchronized kroz JVM instrukcije monitorenter i monitorexit osigurava da instrukcije u bloku koda ne budu preuređene.
Objasniću ovako, na primer za:
synchronized (lock) {
x = 1;
flag = true;
}Pseudo-kod nakon javap dekompajlacije:
monitorenter // Dobija bravanu
store x, 1 // Promenljiva x = 1
store flag, true // Promenljiva flag = true
monitorexit // Oslobađa bravanuStvarni rezultat nakon javap dekompajlacije:

Objašnjenje instrukcija:
| Instrukcija | Funkcija |
|---|---|
| monitorenter | Dobija bravanu, ulazi u sinhronizovani blok koda |
| iconst_1 | Gura integer 1 na stek operanda |
| istore_1 | Čuva 1 u lokalnoj promenljivoj x |
| iconst_1 | Ponovo gura integer 1 na stek operanda |
| istore_2 | Čuva 1 u lokalnoj promenljivoj flag |
| aload 4 | Učitava referencu na lock objekat |
| monitorexit | Oslobađa bravanu, izlazi iz sinhronizovanog bloka koda |
Kako synchronized realizuje reentrant?
Reentrant znači da ista nit može više puta dobiti istu bravanu, a neće biti blokirana.

synchronized podržava reentrant zato što Java zaglavlje objekta sadrži Mark Word, koji se koristi za čuvanje stanja objekta, uključujući informacije o bravi.
Kada nit dobije objektnu bravanu, JVM će upisati ID te niti u Mark Word i postaviti brojač brave na 1.
Ako nit pokuša ponovo da dobije bravu koju već drži, JVM će proveriti ID niti u Mark Word-u. Ako se ID poklapa, to znači da je ista nit, brojač brave se povećava.
Kada nit izađe iz sinhronizovanog bloka, brojač brave se smanjuje. Ako je vrednost brojača nula, JVM će označiti bravanu kao nedrženu i obrisati informacije o ID-u niti.
Objasniću primerom:
class ReentrantExample {
public synchronized void method1() {
System.out.println("Method1 dobila bravanu");
method2(); // Nit već drži bravanu, može nastaviti da poziva method2
}
public synchronized void method2() {
System.out.println("Method2 dobila bravanu");
}
public static void main(String[] args) {
ReentrantExample example = new ReentrantExample();
example.method1();
}
}Rezultat izvršenja:
Method1 dobila bravanu
Method2 dobila bravanuZato što synchronized podržava reentrant, method1 nakon dobijanja brave može dobiti bravanu i method2.
Na nižem nivou se realizuje kroz _owner i _count polja Monitor objekta, _owner beleži nit koja drži bravu, _count beleži koliko puta je nit dobila bravanu.
+----------------------+
| ObjectMonitor |
| ---------------- |
| _owner = Thread-1 | // Trenutna nit koja drži bravu
| _count = 2 | // Nit je reentrant 2 puta
+----------------------+
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Kuaishou studenta 5 za intervju: Kako synchronized reentrant brava realizuje
memo: 10. februar 2025. izmenjeno do ovde.
29.🌟 Jeste li upoznati sa nadogradnjom synchronized brave?
Preporučeno čitanje: Šta su pristrasna brava, laka brava, teška brava?
U JDK 1.6, radi poboljšanja synchronized performansi, uveden je mehanizam nadogradnje brave, od niskotroškovne brave do maksimalnog smanjenja konkurencije za bravanu.

Kada nema konkurencije niti, koristi se niskotroškovna "pristrasna brava", tada nema dodatnih CAS operacija; pri lakoj konkurenciji koristi se "laka brava", primenjuje se CAS spinning, izbjegava se blokiranje niti; samo pri teškoj konkurenciji koristi se "teška brava", realizuje se kroz Monitor mehanizam, potrebno je blokiranje niti.
Zašto synchronized nema degradaciju brave?
Glavni razlog je što degradacija ne donosi veliku korist. Degradacija nije jednostavna promena markera. Teška brava uključuje mutex na nivou operativnog sistema, redove čekanja, blokirane niti. Za degradaciju je potrebno osigurati:
- Nema niti u redu čekanja
- Trenutno nema konkurencije
- Sigurno osloboditi sistemske resurse
- Ponovo inicijalizirati stanje lake brave
Ovaj komplet proces provere i operacija donosi veliki trošak. I kad da proveravaš? Svaki put pri oslobađanju brave? Gubitak performansi je prevelik.
Jeste li upoznati sa četiri stanja synchronized brave?
Upoznat/a.
①. Stanje bez brave, objekat nije zaključan, Mark Word čuva hash kod objekta i druge informacije.
②. Pristrasna brava, kada nit prvi put dobija bravanu, ulazi se u pristrasni mod. Mark Word beleži ID niti, naredna ista nit ponovo može ući u synchronized zaključani kod, bez dodatnog zaključavanja.

③. Laka brava, kada više niti u različito vreme dobija istu bravanu, tj. ne postoji konkurencija za bravanu, JVM će koristiti laku bravanu da bi izbjegao blokiranje niti.
Nit koja ne drži bravanu čeka CAS spinning da se brava oslobodi.

Kada nit uđe u synchronized zaključani kod, ako je stanje brave objekta pristrasna brava, tj. tip brave je "01", marker pristrasne brave je "0".

Zatim se primenjuje CAS spinning način, pokušava se zameniti Mark Word u zaglavlju objekta pokazivačem na Lock Record, a pokazivač owner u Lock Record-u ukazuje na Mark Word objekta.

Ako ova zamena uspe, nit je dobila bravanu tog objekta, marker tipa brave u zaglavlju objekta Mark Word se ažurira na "00", što označava da je objekat u stanju lake brave.
④. Teška brava, ako se spinning određen broj puta ili jedna nit drži bravanu, jedna se spin, a treća nit uđe u synchronized zaključani kod, laka brava će se nadograditi u tešku bravanu.
U tom trenutku, tip brave u zaglavlju objekta se ažurira na "10", Mark Word će čuvati pokazivač na Monitor objekat, sve druge niti koje čekaju bravanu ulaze u blokirano stanje.
Koje optimizacije synchronized radi?
U JDK 1.6 i ranije, synchronized je direktno pozivao ObjectMonitor enter i exit instrukcije za realizaciju, ova brava se takođe zove teška brava, performanse su slabe.
S ažuriranjem JDK verzije, synchronized performanse su značajno optimisane:
①. Pristrasna brava: Ista nit može više puta dobiti istu bravanu, nije potrebno ponovljeno zaključavanje.
②. Laka brava: Kada nema konkurencije niti, kroz CAS spinning čeka oslobađanje brave, izbjegava se direktan ulazak u blokirano stanje.
③. Eliminacija brave: JIT može tokom izvršenja analizirati kod, ako uoči da određene operacije brave ne mogu biti istovremeno pristupljene od strane više niti, uklonit će te brave, čime se smanjuju troškovi zaključavanja.
Molim vas detaljno da pričate o procesu nadogradnje brave?
Odgovor u stanju konfuzije: nadogradnja brave ide od bez brave do pristrasne brave, zatim do lake brave, konačno do teške brave.

Odgovor sa nekim znanjem, ali ne dubokim:

①. Pristrasna brava: kada nit prvi put dobija bravanu, JVM će u Mark Word zaglavlju objekta zabeležiti ID te niti, sledeći put pri ulasku u synchronized, ako je i dalje ista nit, može se direktno izvršiti, bez dodatnog zaključavanja.
②. Laka brava: kada više niti pokušava da dobije bravanu ali ne u istom periodu, pristrasna brava će se nadograditi u laku bravanu, nit koja čeka bravanu kroz CAS spinning izbjegava ulazak u blokirano stanje.
③. Teška brava: ako spinning ne uspe, brava će se nadograditi u tešku bravanu, nit koja čeka bravanu ulazi u blokirano stanje, čeka Monitor za raspodelu.
Detaljno objašnjenje:
①. Od bez brave do pristrasne brave:
Kada nit prvi put pristupa sinhronizovanom kodu, ako je ovaj objekat u stanju bez brave i pristrasna brava nije onemogućena, JVM će promeniti marker brave u zaglavlju objekta u stanje pristrasne brave i zabeležiti ID trenutne niti. U ovom trenutku, u Mark Word zaglavlju objekta čuva se ID niti koja drži pristrasnu bravanu.
Ako druga nit pokuša da dobije ovu pristrasnu bravanu, JVM će proveriti da li je trenutna nit koja drži pristrasnu bravanu aktivna. Ako nit koja drži pristrasnu bravanu nije aktivna, može se brava preusmeriti novoj niti; u suprotnom se opoziva pristrasna brava i nadograđuje u laku bravanu.
②. Od pristrasne do lake brave:
Pri opozivu pristrasne brave, prolazi se kroz sve zapise o bravi u steku, pauzira se nit koja drži pristrasnu bravanu i proverava objekat brave. Ako se u ovom procesu uoči da druge niti pokušavaju da dobiju ovu bravanu, JVM će opozvati pristrasnu bravanu i nadograditi bravanu u laku bravanu.
Kada dva ili više niti se takmiče za istu pristrasnu bravanu, pristrasni mod više nije efikasan, u ovom trenutku se pristrasna brava opoziva, stanje brave objekta se nadograđuje u laku bravanu.
③. Od lake do teške brave:
Laka brava čeka oslobađanje brave kroz spinning. Ako se spinning određeni broj puta (broj spinning je podesiv i adaptivan, ako više puta ne uspe, spinning je manji), označava jaka konkurencija za bravanu.
Kada se spinning više puta ne uspe ili postoje niti u redu čekanja koje čekaju istu laku bravanu, laka brava se nadograđuje u tešku bravanu. U ovom slučaju, JVM će na nivou operativnog sistema kreirati mutex bravanu — Mutex, sve dalje niti koje pokušavaju da dobiju ovu bravanu biće blokirane dok se brava ne oslobodi.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Xiaomi prolećni regrutacije studenta K za prvi intervju: Proces nadogradnje synchronized brave
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Agricultural Bank of China studenta 1 za intervju: Optimizacija Java brava
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Qunar studenta 1 za tehnički drugi intervju: Nadogradnja brave, synchronized osnovni princip, da li se dodiruje OS nivo
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Kuaishou studenta 2 za prvi intervju: Proces nadogradnje brave?
memo: 11. septembar 2025. izmenjeno do ovde. Nadogradnja synchronized brave je veoma važan sadržaj, druga izdanja optimizacije su reorganizovala ovaj sadržaj, lično smatram da je lakše razumeti, čekam efekte. Danas jedan Tencent član kluba koji je prešao na stalno zaposlenje u mail-u za izmenu CV-a pominje: Mian Czhi Nixi je zaista dobro napisao, zaista hvala na priznanju.

30.🌟 Jeste li upoznati sa razlikom između synchronized i ReentrantLock?
Dvije reči odgovora: synchronized realizuje Monitor mehanizam unutar JVM-a, ReentrantLock se bazira na AQS.
synchronized može automatski zaključavati i otključavati, ReentrantLock zahteva ručno lock() i unlock().

Ako intervjuer želi da sazna više, možete nastaviti odgovor:
①. ReentrantLock može realizovati višestruki selektivni notifikacijski mehanizam, vezuje više Condition, dok synchronized može samo kroz wait i notify probuditie, pripada jednostrukom notifikacijskom mehanizmu;
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();②. synchronized može zaključavati na metodima i blokovima koda, ReentrantLock može samo zaključavati blokove koda, ali može specificirati da li je reći o fair bravi ili nefair bravi.
// synchronized modifikuje metod
public synchronized void method() {
// Poslovni kod
}
// synchronized modifikuje blok koda
synchronized (this) {
// Poslovni kod
}
// ReentrantLock zaključavanje
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// Poslovni kod
} finally {
lock.unlock();
}③. ReentrantLock pruža mehanizam prekida niti koje čekaju bravanu, realizuje se kroz lock.lockInterruptibly().
ReentrantLock lock = new ReentrantLock();
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
// Obrada prekida izuzetka
}Kod velike konkurencije, da li koristiti synchronized ili ReentrantLock?
Više incliniram ka ReentrantLock, zato što:
- ReentrantLock pruža karakteristike kao što su tajmaut i fair brava, može se nositi sa kompleksnijim konkurentnim scenama.
- ReentrantLock dozvolja finiju kontrolu brave, efektivno smanjuje konkurentnu borbu za bravanu.
- ReentrantLock podržava uslovne promenljive Condition, može realizovati prijatniji mehanizam komunikacije između niti nego synchronized.
Jeste li upoznati sa Lock?
Lock je interfejs u JUC-u, najčešće korišćene implementacije uključuju reentrant bravanu ReentrantLock, bravanu čitanja-pisanja ReentrantReadWriteLock itd.
Jeste li upoznati sa realizacionom logikom ReentrantLock lock() metode?
Konkretnu realizaciju lock metoda realizuje internja Sync klasa ReentrantLock-a, uključuje spinning niti, blokirani red, CAS, AQS itd.

lock metod će prvo pokušati da dobije bravanu kroz CAS. Ako trenutna brava nije držana, postaviće stanje brave na 1, što označava da je brava zauzeta. U suprotnom, dodaće trenutnu nit u AQS red čekanja.
final void lock() {
if (compareAndSetState(0, 1)) // Pokušava direktno da dobije bravanu
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // Ako dobijanje ne uspe, ulazi u AQS red čekanja
}
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Xiaomi prolećne regrutacije studenta K za prvi intervju: razlika između synchronized i lock
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Xiaomi studenta F za intervju: razlika i scenario između synchronized i ReentrantLock
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Dewu studenta 8 za prvi intervju: Kod posebno velike konkurencije da li koristiti synchronized ili ReentrantLock
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Pinduoduo studenta 4 za tehnički prvi intervju: Java višenitnost, sinhronizacija i ekstlukcija
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Kuaishou studenta 2 za prvi intervju: Jeste li upoznati sa Lock? Koja je konkretna realizacija logika Lock.lock()?
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Li Auto studenta 2 za prvi intervju: synchronized VS ReentrantLock VS CAS
31. Koliko poznajete AQS?
Preporučeno čitanje: Šta je AQS?
AQS je apstraktna klasa, održava deljenu promenljivu state i red čekanja niti, pruža podršku nižeg nivoa za klase poput ReentrantLock.

Ideja AQS-a je: ako se zatraženi deljeni resurs nalazi u slobodnom stanju, trenutna nit uspešno dobija bravanu; u suprotnom, dodaje trenutnu nit u red čekanja, kada druge nite oslobode bravanu, bira jednu nit iz reda čekanja i dodeljuje joj bravanu.
Jeste li čitali AQS izvorni kod?
Istraživao/la.
Prvo, state stanje modifikovano volatile promenljivom, koristi se za osiguravanje vidljivosti između više niti;
private volatile int state;②. Sinhronizovani red realizuje interno definisana Node klasa, svaki Node uključuje stanje čekanja, prethodni i sledeći čvor, referencu na nit itd., to je dvostruko vezana FIFO lista.
static final class Node {
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile Node prev;
volatile Node next;
volatile Thread thread;
}AQS podržava dva načina sinhronizacije:
- Režim ekskluzivnosti: U svakom trenutku samo jedna nit može držati bravanu, na primer ReentrantLock.
- Režim deljenja: Više niti može istovremeno dobiti bravanu, na primer Semaphore i CountDownLatch.
Ključni metodi uključuju:
acquire: Dobija bravanu, neuspeh ulazi u red čekanja;release: Oslobađa bravanu, budi nit u redu čekanja;acquireShared: Dobija bravanu u režimu deljenja;releaseShared: Oslobađa bravanu u režimu deljenja.
AQS koristi CLH red za održavanje niti čekanja, CLJ je skraćenica prvih slova autora Craiga, Landina i Hagerstena, to je vrsta spinning brave zasnovane na listi.

U CLH-u, kada nit pokuša da dobije bravanu i ne uspe, dodaje se na kraj reda i spinning, čekajući da nit prethodnog čvora oslobodi bravanu.

Prednost CLH-a je: pretpostavimo da 100 niti čeka bravanu, nakon oslobađanja brave, obavestiće samo prvu nit u redu da se takmiči za bravanu. Izbegava se istovremeno buđenje velikog broja niti, štedeći CPU resurse.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje WeBank studenta 1 za Java backend prvi intervju: Pričaj o AQS
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Honor studenta 4 za intervju: Pričaj o nekim tehnologijama koje poznaješ (rekao AQS, brava)
memo: 12. februar 2025. izmenjeno do ovde, ostatak vremena danas sam izmenio CV i prilagodio tehnički sektor Pai Congming AI asistent interfejs (povezan sa Deepseek).

32.🌟 Pričaj o realizacionom principu ReentrantLock?
ReentrantLock je reentrant ekskluzivna brava bazirana na AQS, koristi CAS za pokušaj dobijanja brave, neuspeh ulazi u CLH blokirani red, podržava fair bravanu, nefair bravanu, može se prekinuti, čekati tajmaut.

Internim brojačem state prati stanje i broj držanja brave. Kada nit pozove lock() metod da dobije bravanu, ReentrantLock će proveriti vrednost state, ako je 0, kroz CAS menja na 1, označava uspešno zaključavanje. U suprotnom, prema strategiji pravednosti trenutne niti, dodaje se u red čekanja.
Prvi put kada nit dobija bravanu, vrednost state se postavlja na 1; ako ista nit ponovo dobija bravanu, state se povećava za 1; svaki put kada se oslobodi brava, state se smanjuje za 1.
Kada nit pozove unlock() metod, ReentrantLock će smanjiti state brave koju drži za 1, ako state = 0, oslobađa bravanu i budi nit u redu čekanja da se takmiči za bravanu.
Način korišćenja je vrlo jednostavan:
class CounterWithLock {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // Dobija bravanu
try {
count++;
} finally {
lock.unlock(); // Oslobađa bravanu
}
}
public int getCount() {
return count;
}
}new ReentrantLock() podrazumevano kreira nefair bravanu NonfairSync. U nefair modu, brava može biti dodeljena nit koja je upravo zatražila, bez obzira na vreme čekanja. Kada se prebaci na fair mod, brava se dodeljuje nit koja najduže čeka.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Xiaomi prolećne regrutacije studenta K za prvi intervju: fair brava i nefair brava kako realizovati nefair bravanu
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Oppo studenta 8 za backend razvoj jesenja regrutacija prvi intervju: Pričaj o ReentrantLock
33. Kako ReentrantLock kreira fair bravanu?
Vrlo jednostavno, prilikom kreiranja ReentrantLock, prosledi parametar true.
ReentrantLock lock = new ReentrantLock(true);
// true predstavlja fair bravanu, false predstavlja nefair bravanu
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}Kako kreirati nefair bravanu?
Prilikom kreiranja ReentrantLock, ne prosleđuj parametar ili prosledi parametar.
Kaka je razlika između nefair i fair brave?
Dve reči odgovora:
Fair brava znači da se kod takmičenja više niti za bravanu, redosled dobijanja bravery odgovara redosledu zatraživanja brave niti, tj. prvi došao, prvi servisiran.
Nefair brava ne garantuje redosled dobijanja brave niti, kada se brava oslobodi, bilo koja nit koja zatražuje bravanu može je dobiti, a ne prema redosledu zatraživanja.
Jeste li upoznati sa realizacionom logikom fair brave?
Jezgro logike fair brave se nalazi u AQS hasQueuedPredecessors() metodu, ovaj metod se koristi da proveri da li ispred trenutne niti postoje niti koje čekaju.

Ako ispred reda postoje niti koje čekaju, trenutna nit ne može preuzeti bravanu, mora da čeka po redu reda. Ako ispred reda nema niti ili je trenutna nit prva u redu, može dobiti bravanu.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Kuaishou studenta 7 za Java backend tehnički prvi intervju: Predstavite fair i nefair brave
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Xiaomi prolećne regrutacije studenta K za prvi intervju: fair brava i nefair brava kako realizovati nefair bravanu
34.🌟 Koliko poznajete CAS?
Preporučeno čitanje: Potpuno razumite princip Java realizacije CAS
CAS je vrsta optimistic brave, koristi se za poređenje da li je trenutna vrednost promenljive jednaka očekivanoj vrednosti, ako je jednaka, ažurira vrednost, u suprotnom pokušava ponovo.

U CAS postoje tri vrednosti:
- V: Promenljiva koja se ažurira (var)
- E: Očekivana vrednost (expected)
- N: Nova vrednost (new)
Prvo se sudi da li je V jednako E, ako jeste, postavlja vrednost V na N; ako nije, to znači da je druga nit već ažurirala V, trenutna nit odustaje od ažuriranja.
Ova operacija poređenja i zamene mora biti atomična, ne može se prekidati. Java CAS realizuje Unsafe klasa.
compareAndSet metod AtomicInteger klase je CAS metod:
AtomicInteger atomicInteger = new AtomicInteger(0);
int expect = 0;
int update = 1;
atomicInteger.compareAndSet(expect, update);On poziva Unsafe-ov compareAndSwapInt.

Kako osigurati atomičnost CAS-a?
CPU će emitovati LOCK instrukciju za zaključavanje magistrale, sprečava druge procesore da vrše operacije nad memorijskom adresom, dok se trenutna instrukcija ne završi.
lock cmpxchg [esi], eax ; Poredi vrednost na esi adresi sa eax, ako je jednaka zamenjuje
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Huawei studenta 8 za tehnički drugi intervju: Kako se realizuje optimistic brava?
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Ctrip studenta 1 za Java backend tehnički prvi intervju: cas i aba (atomična operacija + vremenska oznaka)
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Tencent studenta 27 za cloud backend tehnički prvi intervju: Šta je CAS algoritam? Kako osigurava atomičnost podataka (na ovo nije odgovorio/la)
35.🌟 Kaki problemi CAS ima?
CAS ima tri klasična problema, ABA problem, veliki trošak spinning, može raditi samo sa jednom promenljivom itd.

Šta je ABA problem?
ABA problem se odnosi na to da je vrednost originalno A, kasnije promenjena u B, pa ponovo promenjena u A, u ovom trenutku CAS će pogrešno smatrati da se vrednost nije promenila.
Nit 1: CAS(A → B), menja promenljivu A → B
Nit 2: CAS(B → A), promenljiva je ponovo A
Nit 3: CAS(A → C), CAS uspe, ali su se podaci već menjali!Može se rešiti ABA problem kroz broj verzije/vremensku oznaku.
Na primer, pri svakom ažuriranju promenljive, ne ažurira samo vrednost promenljive, već i broj verzije. Pri CAS operaciji, ne porede samo vrednost promenljive, već i broj verzije.
class OptimisticLockExample {
private int version;
private int value;
public synchronized boolean updateValue(int newValue, int currentVersion) {
if (this.version == currentVersion) {
this.value = newValue;
this.version++;
return true;
}
return false;
}
}Java AtomicStampedReference dodaje broj verzije, istovremeno proverava da li su i referentna vrednost i stamp jednaki.

Primer korišćenja:
class ABAFix {
private static AtomicStampedReference<String> ref = new AtomicStampedReference<>("100", 1);
public static void main(String[] args) {
new Thread(() -> {
int stamp = ref.getStamp();
ref.compareAndSet("100", "200", stamp, stamp + 1);
ref.compareAndSet("200", "100", ref.getStamp(), ref.getStamp() + 1);
}).start();
new Thread(() -> {
try { Thread.sleep(100); } catch (InterruptedException e) {}
int stamp = ref.getStamp();
System.out.println("CAS rezultat: " + ref.compareAndSet("100", "300", stamp, stamp + 1));
}).start();
}
}Kako rešiti veliki trošak spinning?
CAS će se neprestano spin ponovljeni pokušaj pri neuspehu, ako nikada ne uspe, doneti će veoma velike troškove izvršenja CPU-u.
Može se dodati ograničenje broja spinning, nakon određenog broja, prebaciti se na suspended blokiranje niti.
int MAX_RETRIES = 10;
int retries = 0;
while (!atomicInt.compareAndSet(expect, update)) {
retries++;
if (retries > MAX_RETRIES) {
synchronized (this) { // Prekoračenje broja, koristi synchronized za obradu
if (atomicInt.get() == expect) {
atomicInt.set(update);
}
}
break;
}
}Šta raditi kada je u pitanju istovremeno ažuriranje više promenljivih?
Može se više promenljivih pakovati u jedan objekat, koristiti AtomicReference za CAS ažuriranje.
class Account {
static class Balance {
final int money;
final int points;
Balance(int money, int points) {
this.money = money;
this.points = points;
}
}
private AtomicReference<Balance> balance = new AtomicReference<>(new Balance(100, 10));
public void update(int newMoney, int newPoints) {
Balance oldBalance, newBalance;
do {
oldBalance = balance.get();
newBalance = new Balance(newMoney, newPoints);
} while (!balance.compareAndSet(oldBalance, newBalance));
}
}
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Ctrip studenta 1 za Java backend tehnički prvi intervju: cas i aba (atomična operacija + vremenska oznaka)
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Oppo studenta 15 za intervju uživo: CAS operacija, koje probleme donosi
memo: 13. februar 2025. izmenjeno do ovde, VIP grupa već ima članove kluba koji traže sledeću temu, iskreno, poslednjih dana ima mnogo stvari, ozbiljna izmena će potrajati, zato se nadam da možete razumeti.

36. Koje metode Java koristi za osiguravanje atomičnosti?

Na primer atomične klase koje počinju sa Atomic, synchronized ključnu reč, ReentrantLock brava itd.
37. Koliko poznajete klase atomičnih operacija?
Klase atomičnih operacija se baziraju na CAS + volatile realizaciji, niži nivo zavisi od Unsafe klase, najčešće korišćene su AtomicInteger, AtomicLong, AtomicReference itd.

Kao AtomicIntegerArray one koje se završavaju sa Array, mogu atomički ažurirati elemente u nizu.
class AtomicArrayExample {
public static void main(String[] args) {
AtomicIntegerArray atomicArray = new AtomicIntegerArray(new int[]{1, 2, 3});
atomicArray.incrementAndGet(1); // Samo uvećava indeks 1
System.out.println(atomicArray.get(1)); // Izlaz 3
}
}Kao AtomicStampedReference može kroz broj verzije rešiti ABA problem u CAS-u.
class AtomicStampedReferenceExample {
public static void main(String[] args) {
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 1);
int stamp = ref.getStamp(); // Dobija broj verzije
ref.compareAndSet(100, 200, stamp, stamp + 1); // A → B
ref.compareAndSet(200, 100, ref.getStamp(), ref.getStamp() + 1); // B → A
}
}38. Jeste li čitali AtomicInteger izvorni kod?
Čitao/la.
AtomicInteger se bazira na volatile i CAS realizaciji, niži nivo zavisi od Unsafe klase. Ključni metodi uključuju getAndIncrement, compareAndSet itd.
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}39. Jeste li upoznati sa deadlock niti?
Deadlock se dešava kada više niti međusobno čekaju da oslobe bravanu. Na primer nit 1 drži bravanu R1, čeka bravanu R2; nit 2 drži bravanu R2, čeka bravanu R1.

Jeste li upoznati sa četiri uslova nastanka deadlocka?

Prvi uslov je ekstlukcija: resurs ne može biti deljen od strane više niti, u jednom trenutku ga može koristiti samo jedna nit. Ako jedna nit već koristi resurs, druge niti koje traže taj resurs moraju čekati dok se resurs ne oslobodi.
Drugi uslov je držanje i čekanje: jedna nit već drži jedan resurs i čeka da dobije resurs koji drže druge niti.
Treći uslov je neuzimanje: resurs ne može biti prisilno uzet od niti, mora čekati da sama nit oslobodi resurs.
Četvrti uslov je cirkularno čekanje: postoji lanac čekanja niti, nit A čeka resurs koju drži nit B, nit B čeka resurs koju drži nit C, sve do nit N koja čeka resurs koju drži nit A.
Kako izbeći deadlock?
Prvo, sve niti treba da primene za resurs u fiksnom redosledu. Na primer, prvo aplira za R1 zatim za R2.
Drugo, ako nit uoči da ne može dobiti određeni resurs, može prvo osloboditi resurs koji već drži, zatim ponovo pokuša da applira.
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje iFLYTEK fenomen plan razvoja intervju: Kako izbeći deadlock?
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje ByteDance komercijalni prvi intervju: Šta je deadlock, uslovi nastanka deadlock, razaranje deadlock
40.🌟 Kako otkriti deadlock problem?
Prvo sa nivoa sistema, na primer u Linux produkcionoj okolini, može se prvo koristiti top ps i druge komande da se vidi stanje procesa, da li postoji proces koji zauzima previše resursa.
Zatim, koristiti JDK-u prateće alatke za monitoring performansi, na primer koristiti jps -l da vidiš trenutni proces, zatim koristiti jstack broj procesa da vidiš informacije o steku niti trenutnog procesa, da li neka nit čeka resurs brave.
Takođe možeš koristiti vizuelne alatke za monitoring performansi, na primer JConsole, VisualVM itd., da vidiš stanje izvršenja niti, konkurentnu situaciju brave itd.

Objasnićemo kroz stvarni kod:
class DeadLockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1) {
System.out.println("Nit1 je dobila bravanu1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("Nit1 je dobila bravanu2");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
System.out.println("Nit2 je dobila bravanu2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("Nit2 je dobila bravanu1");
}
}
}).start();
}
}Kreiramo dve niti, svaka nit pokušava da dobije dve brave (lock1 i lock2) po različitom redosledu.
Nekonzistentan redosled dobijanja brave lako dovodi do deadlock. Pokrenite ovaj kod, videćete da obe niti ne mogu nastaviti izvršenje, ulaze u deadlock stanje.

Pokreni jstack pid komandu, možeš videti informacije o nit u deadlock stanju.

Pri kodiranju, pokušaj da koristiš tryLock() umesto lock(), tryLock() može postaviti vreme tajmauta, izbjegava da nit neprestano čeka.
Istovremeno, pokušaj da izbjegneš da jedna nit istovremeno dobija više brave, ako treba više brave, možeš dobijati po fiksnom redosledu.
Preporučeno čitanje:
- JVM alatke za monitoring performansi — komandna linija
- JVM alatke za monitoring performansi — vizuelni
- Alibaba open source Java dijagnostičko čudo Arthas
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje iFLYTEK fenomen plan razvoja intervju: Kako otkriti deadlock?
memo: 14. februar 2025. izmenjeno do ovde.
41. Pričaj o sinhronizaciji i ekstlukciji niti? (dopuna)
- marta 2024. dodato, preporučeno čitanje: NiuKe: Možda je najkompletniji rezime sinhronizacije niti na mreži
Sinhronizacija znači da niti moraju blisko sarađivati, izvršavati zadatke u određenom redosledu. Na primer, nit A se prvo izvršava, zatim nit B.
Ekstlukcija znači da niti moraju da se takmiče za resurse, u istom trenutku samo jedna nit može pristupati deljenom resursu. Na primer, dok nit A pristupa deljenom resursu, nit B ne može pristupati.
Sinhronizacija se fokusira na saradnju između niti, ekstlukcija se fokusira na takmičenje između niti.
Kako realizovati sinhronizaciju i ekstlukciju?
Može se koristiti synchronized ključna reč ili implementaciona klase Lock interfejsa, kao ReentrantLock za zaključavanje resursa.
Brava na nivou operativnog sistema znači Mutex, kada neka nit uđe u kritičnu sekciju, tj. dobije bravanu, druge niti ne mogu ući u kritičnu sekciju, moraju blokirati i čekati da nit koja drži bravanu napusti kritičnu sekciju.

Koje probleme mora rešiti brava?
Prvo, ko može dobiti bravanu, može biti klasa objekat, može biti trenutni this objekat, takođe može biti bilo koji drugi novokreirani objekat.
synchronized (this) {
// Kritična sekcija
}Drugo, pravilo zauzimanja brave, da li se može zauzeti više puta, da li sam može više puta zauzeti.
Treće, šta ako ne može zauzeti, spinning? Blokiranje? Ili odustati nakon tajmauta?
Četvrto, šta sa nitima koja i dalje čekaju nakon oslobađanja brave? Da li obavestiti sve niti zajedno da se takmiče ili samo reći jednoj niti da se takmiči?
Pričaj o spinning bravi?
Spinning brava znači da kada nit pokuša da dobije bravanu, ako je brava već zauzeta, nit neće odmah blokirati, već će kroz spinning, tj. način cikličnog čekanja neprestano pokušavati da dobije bravanu.
Nit1 Nit2
| |
| Dobija bravanu | Pokušava da dobije bravanu
|------------>| (Bravanu je zauzeta, spinning čeka)
| Oslobađa bravanu |
|<------------| Dobija bravanu
| |Pogodna za scenarije gde se brava kratko drži, ReentrantLock tryLock metod koristi spinning bravanu.

Prednost spinning brave je što može izbeći trošak promene konteksta niti, mana je ako se brava dugo drži, dovodi do toga da nit prazno spinning, gubi CPU resurse.
class SpinLock {
private AtomicBoolean lock = new AtomicBoolean(false);
public void lock() {
while (!lock.compareAndSet(false, true)) {
// Spinning čeka, neprestano pokušava da dobije bravanu
}
}
public void unlock() {
lock.set(false);
}
public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
Runnable task = () -> {
spinLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " je dobio bravanu");
} finally {
spinLock.unlock();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}Podrazumevano, spinning brava će čekati sve dok ne dobije bravanu. U stvarnom razvoju, potrebno je postaviti broj spinning ili vreme tajmauta. Ako se pređe prag, nit može odustati od brave ili ući u blokirano stanje.
Da li sinhronizacija i ekstlukcija imaju vremenske zahteve?
Imaju.
Jezgro ekstlukcije je osigurati da u istom trenutku samo jedna nit može pristupiti deljenom resursu.
Sinhronizacija naglašava redosled izvršenja između niti, naročito kada više niti mora zavisiti od rezultata međusobnog izvršenja.
Na primer, u CountDownLatch-u, glavna nit će čekati da se završi više podnit zadataka.
class SyncExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
// Kreiramo 3 podniti
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
Thread.sleep(1000); // Simulacija zadatka
System.out.println("Odigrao Kraljevstvo.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // Svaka nit nakon završetka zadatka smanjuje brojač za 1
}
}).start();
}
System.out.println("Čekam da se odigra tri puta Kraljevstvo pa idem na spavanje...");
latch.await(); // Glavna nit čeka podniti
System.out.println("Dobro, Kraljevstvo je gotovo, mogu spavati");
}
}Nakon što se sve podniti završe, glavna nit nastavlja izvršenje.

- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje iFLYTEK fenomen plan razvoja intervju: Pričaj o sinhronizaciji niti
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Pinduoduo studenta 4 za tehnički prvi intervju: Java višenitnost, sinhronizacija i ekstlukcija, da li sinhronizacija i ekstlukcija imaju vremenske zahteve?
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Hikvision studenta 4 za intervju: Šta je spinning brava, da li spinning brava uvek čeka? Koji su mane spinning brave?
42.🌟 Pričaj o pesimističnoj i optimističnoj bravi? (dopuna)
- maja 2024. dopunjeno
Dobro.
Pesimistična brava smatra da se svaki put prilikom pristupa deljenom resursu može desiti sukob, zato se pre operacije mora prvo zaključati, sprečiti druge niti da menjaju podatke.
Optimistična brava smatra da sukob ne uvek dolazi, zato se pre operacije ne zaključava, već pri ažuriranju podataka proverava da li su druge niti menjale podatke. Ako se uoči da su podaci menjani, ponovit će pokušaj.
Šta ako optimistična brava uoči da je druga nit došla da menja podatke?
Može ponovo pročitati podatke, zatim pokušati da ažurira, sve dok ne uspe ili dostigne maksimalni broj pokušaja.
Pročitaj podatke -> Pokušaj ažuriranje -> Uspeh (vrati uspeh)
|
-> Neuspeh -> Ponovi -> Dostigni maksimalan broj -> Vrati neuspehNapišimo kod za demonstraciju:
class CasRetryExample {
private static AtomicInteger counter = new AtomicInteger(0);
private static final int MAX_RETRIES = 5;
public static void main(String[] args) {
boolean success = false;
int retries = 0;
while (retries < MAX_RETRIES) {
int currentValue = counter.get();
boolean updated = counter.compareAndSet(currentValue, currentValue + 1);
if (updated) {
System.out.println("Ažuriranje uspešno, trenutna vrednost: " + counter.get());
success = true;
break;
} else {
retries++;
System.out.println("Ažuriranje neuspešno, vršim " + retries + ". pokušaj");
}
}
if (!success) {
System.out.println("Dostignut maksimalan broj pokušaja, operacija neuspešna");
}
}
}
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Ali studenta 5 Ali Mama Java backend tehnički prvi intervju: Pričaj o Java konkurentnom sistemu (od pesimistične brave do optimistične brave, i niti, nitni bazen itd., pričalo se skoro deset minuta o ovome)
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Ali studenta 1 Xianyu backend prvi intervju: Optimistična brava, pesimistična brava, ABA problem
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Tencent Cloud pametni studenta 20 za drugi intervju: Kako razumnete optimističnu i pesimističnu bravanu?
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje Hikvision studenta 4 za intervju: Vrste Java brava, u kom scenariju koristiti optimističnu bravanu, u kom scenariju koristiti pesimističnu bravanu? Pri korišćenju optimistične brave ako druga nit dođe da menja podatke, šta treba raditi
memo: 15. februar 2025. izmenjeno do ovde.
Konkurentne alatne klase
43. Jeste li upoznati sa CountDownLatch?
Preporučeno čitanje: Semaphore, Exchanger, CountDownLatch, CyclicBarrier, Phaser, sve obuhvaćeno
CountDownLatch je sinhronizaciona alatna klasa u JUC-u, koristi se za koordinaciju sinhronizacije između više niti, osigurava da glavna nit nastavlja izvršenje nakon što se više podniti završi.
Njena osnovna ideja je kroz brojač povratnog brojanja da kontroliše redosled izvršenja više niti.
class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int threadCount = 3;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
Thread.sleep((long) (Math.random() * 1000)); // Simulacija izvršenja zadatka
System.out.println(Thread.currentThread().getName() + " završeno izvršenje");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // Nit nakon završetka, brojač -1
}
}).start();
}
latch.await(); // Glavna nit čeka
System.out.println("Sve podniti su završile izvršenje, glavna nit nastavlja");
}
}Pri korišćenju, prvo treba inicijalizovati CountDownLatch objekat, specificirati početnu vrednost brojača, označava broj niti koje treba čekati.
Zatim u svakoj podniti nakon završetka zadatka, pozivati countDown() metod, brojač se smanjuje za 1.
Zatim glavna nit poziva await() metod da uđe u blokirano stanje, dok brojač ne bude 0, tj. sve podniti se završe zadatke, glavna nit nastavlja izvršenje.

Uz Honor of Kings primer, kreiraćemo pet niti, predstavljaju Da Qiao, Lan Ling Wang, An Qi La, Ne Zha i Kai. Svaki igrač poziva countDown() metod, označava da je spreman. Glavna nit poziva await() metod, čeka da se svi igrači spreme.
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(5);
Thread daqiao = new Thread(() -> {
System.out.println("Da Qiao je spreman!");
countDownLatch.countDown();
});
Thread lanlingwang = new Thread(() -> {
System.out.println("Lan Ling Wang je spreman!");
countDownLatch.countDown();
});
Thread anqila = new Thread(() -> {
System.out.println("An Qi La je spreman!");
countDownLatch.countDown();
});
Thread nezha = new Thread(() -> {
System.out.println("Ne Zha je spreman!");
countDownLatch.countDown();
});
Thread kai = new Thread(() -> {
System.out.println("Kai je spreman!");
countDownLatch.countDown();
});
daqiao.start();
lanlingwang.start();
anqila.start();
nezha.start();
kai.start();
countDownLatch.await();
System.out.println("Svi su spremni, počinje igra!");
}Pet igrača nakon završetka odbrojavanja istovremeno napada.
private static void waitToFight(CountDownLatch countDownLatch, String name) {
try {
countDownLatch.await(); // Čeka signal ovde pa nastavlja
System.out.println(name + " primio, napad!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println(name + " prekinut");
}
}
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread daqiao = new Thread(() -> waitToFight(countDownLatch, "Da Qiao"), "Nit-Da Qiao");
Thread lanlingwang = new Thread(() -> waitToFight(countDownLatch, "Lan Ling Wang"), "Nit-Lan Ling Wang");
Thread anqila = new Thread(() -> waitToFight(countDownLatch, "An Qi La"), "Nit-An Qi La");
Thread nezha = new Thread(() -> waitToFight(countDownLatch, "Ne Zha"), "Nit-Ne Zha");
Thread kai = new Thread(() -> waitToFight(countDownLatch, "Kai"), "Nit-Kai");
daqiao.start();
lanlingwang.start();
anqila.start();
nezha.start();
kai.start();
try {
Thread.sleep(5000); // Simulacija vreme pripreme
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Glavna nit prekinuta");
}
System.out.println("Neprijateljska vojska još 5 sekundi stižu na bojište, sva vojska napadaju!");
countDownLatch.countDown(); // Šalje signal
}Scenario pitanje: Ako treba pretražiti preko 100.000 podataka, koristeći nitni bazen podeljeno u 20 niti za izvršenje, kako postići da se čeka sve niti da završe pretraživanje, tj. nakon što se završi poslednji rezultat pretraživanja, ispise rezultat?
Vrlo jednostavno, može se koristiti CountDownLatch za realizaciju. CountDownLatch je vrlo pogodan za ovaj scenario.
Prvi korak, kreirati CountDownLatch objekat, početna vrednost postavljena na 20, označava 20 niti treba da završi zadatke.
Drugi korak, kreirati nitni bazen, svaka nit izvršava operaciju pretrage, nakon završetka pretrage pozvati countDown() metod, brojač se smanjuje za 1.
Treći korak, glavna nit poziva await() metod, čeka da se sve niti završe.
class DataQueryExample {
public static void main(String[] args) throws InterruptedException {
// Simulacija 100.000 podataka
int totalRecords = 100000;
int threadCount = 20;
int batchSize = totalRecords / threadCount; // Količina podataka koju svaka nit obrađuje
// Kreiranje nitnog bazena
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
// Simulacija rezultata pretrage
ConcurrentLinkedQueue<String> results = new ConcurrentLinkedQueue<>();
for (int i = 0; i < threadCount; i++) {
int start = i * batchSize;
int end = (i == threadCount - 1) ? totalRecords : (start + batchSize);
executor.execute(() -> {
try {
// Simulacija operacije pretrage
for (int j = start; j < end; j++) {
results.add("Data-" + j);
}
System.out.println(Thread.currentThread().getName() + " obrađuje podatke " + start + " - " + end);
} finally {
latch.countDown(); // Nit završava zadatak, brojač -1
}
});
}
// Čeka sve niti da se završe
latch.await();
executor.shutdown();
// Ispis rezultata
System.out.println("Sve niti su završile izvršenje, ukupan broj rezultata pretrage: " + results.size());
}
}
- Vodič za Java intervjue (plaćeno) sadrži originalno pitanje SF Technology studenta 1 za intervju: Konkurentno programiranje CountDownLatch i red poruka
44. Jeste li upoznati sa CyclicBarrier?
Upoznat/a.
CyclicBarrier bukvalno znači ciklički barijera koja se može koristiti ponovo, koristi se da više niti međusobno čeka, sve dok sve niti ne stignu do barijere zatim istovremeno nastavljaju izvršenje.

Pri korišćenju, treba prvo inicijalizovati CyclicBarrier objekat, specificirati vrednost barijere N, označava broj niti koje treba čekati.
Zatim svaka nit izvršava await() metod, označava da je stigla do barijere, čeka druge niti, u ovom trenutku se vrednost barijere smanjuje za 1.
Kada sve niti stignu do barijere, tj. vrednost barijere je 0, sve niti će nastaviti izvršenje.
class CyclicBarrierExample {
private static final int THREAD_COUNT = 3;
private static final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " stigao do barijere");
barrier.await(); // Nit se blokira, dok sve niti ne stuže
System.out.println(Thread.currentThread().getName() + " nastavlja izvršenje");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}45. Kaka je razlika između CyclicBarrier i CountDownLatch?
CyclicBarrier čeka da sve niti međusobno stužu do barijere zatim nastavljaju; CountDownLatch čeka da glavna nit čeka sve podniti da se završe.
| Poredba | CyclicBarrier | CountDownLatch |
|---|---|---|
| Glavna namena | Čeka da sve niti međusobno stužu do barijere zatim nastavljaju | Čeka da glavna nit čeka sve podniti da se završe |
| Ponovljiva upotreb -ljivost | ✅ Može se koristiti ponovo, nakon otvaranja barijere se automatski resetuje | ❌ Ne može se koristiti ponovo, nakon što brojač dođe do nule ne može se oporaviti |
| Da li se može izvršiti callback | ✅ Može, nakon što sve niti stužu do barijere može se izvršiti barrierAction | ❌ Ne može |
| Stanje čekanja niti | Sve niti međusobno čekaju, jedna nit nije stigla, druge niti će sve blokirati | Glavna nit čeka sve podniti da se završe, podniti se nakon izvršenja mogu nastaviti raditi |
| Primjenjivi scenario | Niti međusobno zavise, treba sinhronizovano izvršenje | Glavna nit čeka završetak više zadataka |
| Scenarij primera | Zadatak proračuna podeljen, sve niti stužu do barijere mogu nastaviti | Glavna nit čeka završetak više inicijalizacija zadataka |
46. Jeste li upoznati sa Semaphore?
Semaphore — semafor, koristi se za kontrolisanje broja niti koje istovremeno pristupa određenom resursu, slično ograničavaču protoka, osigurava da samo određeni broj niti može pristupiti određenom resursu, prekoračenje mora čekati.

Uzmimo parking kao primer.
Parking ima ograničen broj mesta za parkiranje. Ako ima slobodnih mesta, tablica će prikazati broj preostalih mesta, pa vozila mogu ući; u suprotnom će prikazati cifru 0, a nova vozila moraju da čekaju u redu.
Ako neko automobil ode, tablica će ponovo prikazati broj slobodnih mesta, a vozila koja čekaju će ući na parking redom.

Kada koristite Semaphore, prvo treba da inicijalizujete Semaphore objekat i navedete broj dozvola, što predstavlja maksimalni broj niti koje istovremeno mogu pristupiti resursu.
Zatim, pre nego što svaka nit pristupi resursu, pozovite metod acquire() da biste dobili dozvolu. Ako nema dostupnih dozvola, nit će biti blokirana i čekati.
Važno je napomenuti da nakon što završite sa korišćenjem resursa, treba da pozovete metod release() da biste oslobodili dozvolu.
class SemaphoreExample {
private static final int THREAD_COUNT = 5;
private static final Semaphore semaphore = new Semaphore(2); // Najviše 2 niti mogu pristupiti
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // Dobavi dozvolu (ako nema dostupnih dozvola, blokira)
System.out.println(Thread.currentThread().getName() + " pristupa resursu...");
Thread.sleep(2000); // Simulacija izvršenja zadatka
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // Oslobodi dozvolu
}
}).start();
}
}
}Semaphore se može koristiti za kontrolu protoka, kao što su baze podataka za povezivanje, mrežni povezivanje i sl.
Na primer, ako postoji potreba da se pročita podaci iz desetina hiljada datoteka, pošto su svi zadaci intenzivni po U/I, možemo pokrenuti desetine niti da istovremeno čitaju.
Međutim, nakon što se podatke učitaju u memoriju, treba ih sačuvati u bazu podataka, a broj veza sa bazom podataka je ograničen, na primer samo 10, pa moramo kontrolisati broj niti da biste osigurali da istovremeno samo 10 niti koristi veze baze podataka.
U ovom trenutku možete koristiti Semaphore za kontrolu protoka:
class SemaphoreTest {
private static final int THREAD_COUNT = 30;
private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore s = new Semaphore(10);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
s.acquire();
System.out.println("sačuvaj podatke");
s.release();
} catch (InterruptedException e) {
}
}
});
}
threadPool.shutdown();
}
}47.Exchanger poznajete?
Exchanger — razmenjivač, koristi se za razmenu podataka između dve niti.

Podržava dvosmernu razmenu podataka. Na primer, nit A poziva exchange(dataA), a nit B poziva exchange(dataB). One će razmeniti podatke u tački sinhronizacije, odnosno A dobija podatke od B, a B dobija podatke od A.
Ako jedna nit prva pozove exchange(), biće blokirana i čekati, dok druga nit ne pozove exchange().
Kada koristite Exchanger, prvo treba da kreirate Exchanger objekat, a zatim u dve niti pozovete metod exchange() da biste izvršili razmenu podataka.
class ExchangerExample {
private static final Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(() -> {
try {
String threadAData = "Podaci A";
System.out.println("Nit A pre razmene:" + threadAData);
String received = exchanger.exchange(threadAData);
System.out.println("Nit A dobijenih podataka:" + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
String threadBData = "Podaci B";
System.out.println("Nit B pre razmene:" + threadBData);
String received = exchanger.exchange(threadBData);
System.out.println("Nit B dobijenih podataka:" + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}Exchanger se može koristiti za genetske algoritme, a takođe i za proveru tačnosti, na primer kada unosimo papirna bankarska izvoda u elektronsku banku ručno, da bismo izbegli greške, možemo uneti dvaput, a zatim koristiti Exchanger za proveru dva uneta rezultata.
class ExchangerTest {
private static final Exchanger<String> exgr = new Exchanger<String>();
private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String A = "Bankarski izvod A"; // A unosi podatke bankarskog izvoda
exgr.exchange(A);
} catch (InterruptedException e) {
}
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String B = "Bankarski izvod B"; // B unosi podatke bankarskog izvoda
String A = exgr.exchange("B");
System.out.println("Da li su podaci A i B konzistentni:" + A.equals(B) + ", A unos:"
+ A + ", B unos:" + B);
} catch (InterruptedException e) {
}
}
});
threadPool.shutdown();
}
}memo: 18. februar 2025. izmenjeno do ovde.
48.🌟Možete li reći nešto o implementaciji ConcurrentHashMap? (dopuna)
- mart 2024. dodato, premešteno iz poglavlja o okvirima zbirki.
Dobro. ConcurrentHashMap je verzija HashMap koja je sigurna za niti.
JDK 7 koristi segmentne brave, cela Map se deli na više segmenata, svaki segment može nezavisno da se zaključa. Različite niti mogu istovremeno da rade na različitim segmentima, čime se ostvaruje konkurentnost.

JDK 8 koristi precizniju bravu — bravu po korpi, uz CAS + synchronized blok za kontrolu konkurentnog upisa, čime se maksimalno smanjuje konkurencija brava.

Za operacije čitanja, ConcurrentHashMap koristi volatile promenljive za osiguravanje vidljivosti memorije.
Za operacije upisa, ConcurrentHashMap prvo koristi CAS za pokušaj umetanja, ako uspe direktno vraća; u suprotnom koristi synchronized blok za zaključavanje i obradu.
memo: 19. septembar 2025. izmenjeno do ovde, danassaputnik u VIP grupije izvestio da je dobio ponudu od Meituan-a, tako da morao da čestitam 🎉, sada je sredina septembra, svi morate da izdržite, pobeda je pred vratima.

Kako radi implementacija ConcurrentHashMap u JDK 7?
Dobro.
ConcurrentHashMap u JDK 7 koristi segmentne brave, cela Map se deli na više segmenata, svaki segment može nezavisno da se zaključa, svaki segment je sličan Hashtable-u.

Svaki segment održava niz parova ključ-vrednost HashEntry<K, V>[] table, HashEntry je jednosmerna povezana lista.
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
final HashEntry<K,V> next;
}Segment nasleđuje ReentrantLock, tako da je svaki segment ponovljiva brava, različite niti mogu istovremeno da rade na različitim segmentima, čime se ostvaruje konkurentnost.
static final class Segment<K,V> extends ReentrantLock {
transient volatile HashEntry<K,V>[] table;
transient int count;
}Kako teke put proces u ConcurrentHashMap JDK 7?
Put proces je vrlo sličan HashMap-u, samo što se prvo locira određeni segment, a zatim se operiše kroz ReentrantLock. Ukupno se može podeliti u 4 koraka:
Prvi korak, izračunava se hash ključa, locira se segment, ako je segment prazan, prvo se inicijalizuje;
Drugi korak, koristi se ReentrantLock za zaključavanje, ako zaključavanje ne uspe, nit se spin-a, ako se premaši broj spin-ova, blokira, osiguravajući da će se sigurno dobiti brava;
Treći korak, prolazi se kroz parove ključ-vrednost HashEntry u segmentu, ako su ključevi isti, direktno se zamenjuju, ako ključ ne postoji, umeće se.
Četvrti korak, oslobađa se brava.

Kako teke get proces u ConcurrentHashMap JDK 7?
Get je još jednostavniji, prvo se izračuna hash ključa da bi se prošao segment, zatim se prolazi kroz parove ključ-vrednost u segmentu, ako se pronađe, direktno se vraća vrednost.
Get ne zahteva zaključavanje, jer je value volatile, tako da neće doći do problema vidljivosti kada nit čita vrednost.
Kako radi implementacija ConcurrentHashMap u JDK 8?
Dobro.
ConcurrentHashMap u JDK 8 ukida segmentne brave i koristi CAS + synchronized za ostvarivanje preciznije brave po korpi, a koristi crveno-crno stablo za optimizaciju liste kako bi se poboljšao efikasnost prilikom haš kolizija, performanse su značajno poboljšane u odnosu na JDK 7.
Kako teke put proces u ConcurrentHashMap JDK 8?

Prvi korak, izračunava se hash ključa da bi se odredila pozicija korpe u nizu. Ako je niz prazan, koristi se CAS način za inicijalizaciju kako bi se osiguralo da samo jedna nit inicijalizuje niz.
// izračunavanje hash-a
int hash = spread(key.hashCode());
// inicijalizacija niza
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// izračunavanje pozicije korpe
int i = (n - 1) & hash;Drugi korak, ako je korpa prazna, direktno se umeće čvor CAS-om. Ako CAS operacija ne uspe, degeneriraće se u synchronized blok za umetanje čvora.
// CAS umetanje čvora
if (tabAt(tab, i) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break;
}
// u suprotnom, koristi se synchronized blok za umetanje čvora
else {
synchronized (f) { // **zaključava samo trenutnu korpu**
if (tabAt(tab, i) == f) { // osigurava da nije modifikovano od strane drugih niti
if (f.hash >= 0) { // obrada liste
for (Node<K,V> e = f;;) {
K ek;
if (e.hash == hash && ((ek = e.key) == key || (key != null && key.equals(ek)))) {
e.val = value;
break;
}
e = e.next;
}
} else if (f instanceof TreeBin) { // **obrada crveno-crnog stabla**
((TreeBin<K,V>) f).putTreeVal(hash, key, value);
}
}
}
}Tokom procesa umetanja procenjuje se da li je hash korpe manji od 0 (f.hash >= 0), manje od 0 znači da je crveno-crno stablo, veće ili jednako 0 znači da je lista.
Ovde jedno dodatno pojašnjenje: u implementaciji ConcurrentHashMap-a, hash vrednost čvora crveno-crnog stabla TreeBin je fiksirana na -2.

Treći korak, ako dužina liste premaši 8, pretvara se u crveno-crno stablo.
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);Četvrti korak, nakon umetanja novog čvora, poziva se metod addCount() da bi se proverilo da li je potrebno proširenje.
addCount(1L, binCount);Kako teke get proces u ConcurrentHashMap JDK 8?
Get takođe koristi hash ključa za lokaciju, ako se hash čvora na toj poziciji poklapa i ključevi su jednaki, direktno se vraća vrednost.

Ako je hash čvora negativan, to znači da je poseban čvor, na primer čvor stabla ili čvor koji se seli, zatim se poziva find metod za pretragu.

U suprotnom se prolazi kroz listu za traženje odgovarajućeg ključa. Ako se ne pronađe, vraća se null.
Koje su razlike između HashMap i ConcurrentHashMap?
HashMap nije siguran za niti, u okruženju sa više niti treba koristiti ConcurrentHashMap.
Kako koristite ConcurrentHashMap u projektu?
Utehničko-praktičnom projektu, mnogi mesta koriste ConcurrentHashMap, na primer u asihronoj alatskoj klasi AsyncUtil, koristimo ConcurrentHashMap za čuvanje imena zadataka i njihovog vremena izvršenja kako bismo mogli posmatrati i analizirati izvršenje zadataka.

Koja poboljšanja ima ConcurrentHashMap u odnosu na HashMap?
Prvo, u metodu izračunavanja hash-a, spread metod ConcurrentHashMap-a prima već izračunati hashCode, a zatim vrši XOR operaciju između visokih 16 bitova tog hash koda i samog sebe.
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}U odnosu na hash izračunavanje HashMap-a, dodata je operacija & HASH_BITS. Ovde je HASH_BITS konstanta, vrednost je 0x7fffffff, ona osigurava da je rezultat ne-negativan ceo broj.
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}Pored toga, ConcurrentHashMap dalje enkapsulira čvor Node, na primer koristi Forwarding Node za predstavljanje čvora koji se trenutno proširuje.
static final class ForwardingNode<K,V> extends Node<K,V> {
final Node<K,V>[] nextTable;
ForwardingNode(Node<K,V>[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
}Konačno, put metod koristi CAS + synchronized blok za konkurentno upisivanje.

Zašto ConcurrentHashMap u JDK 1.7 koristi ReentrantLock, a u JDK 1.8 koristi synchronized
ConcurrentHashMap u JDK 1.7 koristi mehanizam segmentnih brava, svaki Segment nasleđuje ReentrantLock, što omogućava da svaki Segment može nezavisno da se zaključa.
U JDK 1.8, ConcurrentHashMap ukida Segment segmentne brave i koristi precizniju bravu — bravu po korpi, kao i CAS bezbravni algoritam, svaka korpa može nezavisno da se zaključa, synchronized blok se koristi samo kada CAS ne uspe, čime se smanjuje konkurencija brava i poboljšava konkurentna performansa.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje drugog tehničkog intervjua Huaveija saputnika 8: Da li je ConcurrentHashMap pesimistična ili optimistična brava?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua Kuaishou-a saputnika 7: Koje su razlike između HashMap-a i CurrentHashMap-a
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua Jingdong-a saputnika 1: Princip ConcurrentHashMap-a, kako ga koristite u projektu
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Tencent Cloud Zhizhao-a saputnika 16: Principi implementacije ConcurrentHashMap-a i CopyOnWriteArrayList-e?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog letnjeg praktičnog intervjua Ctrip-a saputnika 10: Kako ConcurrentHashMap garantuje sigurnost niti? Razlike između 1.7 i 1.8
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua odeljenja glavne stanice Kuaishou-a saputnika 1: Koja optimizacija ConcurrentHashMap-a u odnosu na HashMap-a? Gde su optimizacije ConcurrentHashMap-a 1.8 u odnosu na 1.7?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua Huaveija saputnika 11: Kako concurrenthashmap garantuje sigurnost niti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Dewu-a saputnika 8: Rekoo ste da ReentrantLock ima veću performansu od synchronized-a u visoko konkurentnom okruženju, zašto onda ConcurrentHashMap u JDK 1.7 koristi ReentrantLock, a u JDK 1.8 koristi synchronized
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua pozadinskog razvoja jesenske regrutacije OPPO-a saputnika 8: Obratite princip implementacije concurrenthashmap-a
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Kuaishou-a saputnika 2: Koje su sigurne Map-e za niti? Kako ConcurrentHashMap ostvaruje sigurnost niti? Za šta segmentiranje? Nije li dovoljno jedna brava?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua OPPO-a saputnika 1: Da li ConcurrentHashMap ostvaruje sigurnost niti kroz mehanizam brava?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Kuaishou-a saputnika 4: Upravo ste pomenuli da Spring koristi ConcurrentHashMap za ostvarivanje singleton patterna, recite aproximativno tok metoda put i get ConcurrentHashMap-a?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua pozadinskog razvoja Java-e Tencent-a saputnika 29: Kako ConcurrentHashMap radi na nižem nivou?
memo: 20. februar 2025. izmenjeno do ovde, danas trebada izmenim mnogo rezimea, tako da napredak "Mijan zang sil" mora malo da se odloži.

49.Kako ConcurrentHashMap garantuje vidljivost? (dopuna)
- mart 2024. dodato
U ConcurrentHashMap-u, u čvoru Node, value i next su volatile, što osigurava da će ažuriranja value ili next odmah biti vidljiva drugim nitima.
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V value;
volatile Node<K,V> next;
}50.Zašto ConcurrentHashMap ima veću efikasnost od Hashtable-a (dopuna)
- mart 2024. dodato, premešteno iz okvira zbirki u konkurentno programiranje
Hashtable u svakom trenutku dozvoljava samo jednoj niti da pristupa celoj Map-i, i ostvaruje sigurnost niti tako što zaključava celu Map-u. Na primer, metodi get i put direktno imaju synchronized ključnu reč na metodi.
public synchronized V put(K key, V value) {
if (value == null) throw new NullPointerException();
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % table.length;
...
return oldValue;
}Dok se ConcurrentHashMap u JDK 8 ostvaruje korišćenjem CAS + synchronized, zaključava se samo kada je potrebno.
Na primer pri put prvo pokušava da umetne korišćenjem CAS-a, ako ne uspe onda koristi synchronized blok za zaključavanje.
Pri get je potpuno bez brava, jer je value modifikovano sa volatile promenljivom, što garantuje vidljivost memorije.
public V get(Object key) {
int hash = spread(key.hashCode());
Node<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
Node<K,V> e = tabAt(tab, index);
if (e != null) {
do {
if (e.hash == hash && (e.key == key || (key != null && key.equals(e.key)))) {
return e.value; // čitanje volatile promenljive, garantuje vidljivost
}
} while ((e = e.next) != null);
}
return null;
}
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervju-a za prolećnu regrutaciju Xiaomi-a saputnika K: Koje su sigurne map-e za niti, kako ConcurrentHashMap garantuje sigurnost niti, zašto je efikasniji od hashTable-a
51.Možete li reći nešto o principu implementacije CopyOnWriteArrayList-e? (dopuna)
- april 2024. dodato, preporučeno za čitanje: CopyOnWriteArrayList — pobijedite intervjuera za konkurentno programiranje u Java-i
CopyOnWriteArrayList je verzija ArrayList koja je sigurna za niti, pogodna za scenarije sa mnogo čitanja i malo pisanja.
Osnovna ideja CopyOnWrite-a je da se pri operaciji pisanja kreira novi niz, nakon modifikacije se zameni originalni niz, čime se osigurava da operacije čitanja ne zahtevaju brave, čime se poboljšava konkurentna performansa.

Interno koristi volatile promenljivu za modifikovanje niza array, kako bi se osigurala vidljivost memorije pri operacijama čitanja.
private transient volatile Object[] array;Pri operaciji pisanja koristi se ReentrantLock za garantuje sigurnosti niti.
public boolean add(E e) {
final ReentrantLock lock = this.lock;
// zaključavanje
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// kreiranje novog niza
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
// zamena originalnog niza
setArray(newElements);
return true;
} finally {
// oslobađanje brave
lock.unlock();
}
}Mana je što pri operaciji pisanja treba kopirati novi niz, ako je niz velik, performansa operacije pisanja će biti uticana.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Tencent Cloud Zhizhao-a saputnika 16: Principi implementacije ConcurrentHashMap-a i CopyOnWriteArrayList-e?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje letnjeg praktičnog intervjua WeChat Pay-a Tencent-a saputnika 26: Recite o čestim konkurentnim kontejnerima
memo: 19. avgust 2025. izmenjeno do ovde, danas kadasam pomagao saputniku da izmeni rezime, naišao sam na takvu pozitivnu povratnu informaciju: krajem juna zahvaljujući rezimeu koji je Erge promenio, dobio sam praksu u maloj firmi sa skoro hiljadu ljudi, zahvalnost jednom talasu. Praksa se proširilana Pai Congming, upravo se poklapa sa pametnim asistentom kompanije.

52. Možete li reći nešto o BlockingQueue-u? (dopuna)
- avgust 2024. dodato, premešteno iz okvira zbirki u konkurentno programiranje
BlockingQueue je red siguran za niti pod JUC paketom, podržava blokirajući model "producent-potrošač".
Kada je kontejner reda pun, nit proizvođača će biti blokirana, dok potrošačka nit ne uzme element; kada je kontejner reda prazan, nit potrošača će biti blokirana, dok red ne postane ne-prazan.
BlockingQueue ima mnogo implementacionih klasa, kao što su ArrayBlockingQueue, PriorityBlockingQueue i dr.
| Implementaciona klasa | Struktura podataka | Da li je ograničena | Karakteristike |
|---|---|---|---|
| ArrayBlockingQueue | Niz | ✅ Ograničena | Bazirana na nizu, fiksiran kapacitet, FIFO |
| LinkedBlockingQueue | Lista | ✅ Može biti ograničena (podrazumevano Integer.MAX_VALUE) | Bazirana na listi, veća propusnost od ArrayBlockingQueue-a |
| PriorityBlockingQueue | Gomila (red prioriteta) | ❌ Neograničena | Elementi sortirani po prioritetu (ne FIFO) |
| DelayQueue | Red prioriteta (baziran na Delayed interfejsu) | ❌ Neograničena | Elementi se mogu uzeti samo nakon isteka roka |
| SynchronousQueue | Bez bafera | ✅ Kapacitet 0 | Mora se razmeniti podaci 1-na-1, pogodno za predaju zadataka visoke propusnosti |
| LinkedTransferQueue | Lista | ❌ Neograničena | Podržava tryTransfer(), podaci se odmah predaju potrošaču |
Kako se implementuje blokirajući red?
Blokirajući red koristi ReentrantLock + Condition za osiguravanje konkurentne bezbednosti.
Na primer ArrayBlockingQueue interno održava niz, koristi dva pokazivača koji ukazuju na početak i kraj reda.
Pri put prvo koristi ReentrantLock za zaključavanje, zatim proverava da li je red pun, ako je pun blokira i čeka, u suprotnom umeće element.
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
public void put(E e) throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); // 🔹 zaključavanje, osigurava sigurnost niti
try {
while (count == items.length) { // 🔹 red pun, blokira
notFull.await();
}
enqueue(e); // 🔹 umetanje elementa
} finally {
lock.unlock(); // 🔹 oslobađanje brave
}
}
- Vodič za intervjue za Java (plaćeno) sadrži pitanje letnjeg praktičnog intervjua WeChat Pay-a Tencent-a saputnika 26: Recite o čestim konkurentnim kontejnerima
memo: 21. februar 2025. izmenjeno do ovde. Danas je glavni posao jošuvek izmena rezimea, upravo je vrhunac letnje prakse.
Bazen niti
53.🌟Šta je bazen niti?
Bazen niti je alat za upravljanje i ponovno korišćenje niti, može smanjiti troškove stvaranja i uništavanja niti.

U Java-i, ThreadPoolExecutor je osnovna implementacija bazena niti, kontroliše stvaranje i izvršenje niti kroz broj osnovnih niti, maksimalni broj niti, red zadataka i strategiju odbijanja.
Na primer: kao što otvorite restoran, bazen niti odgovara fiksiranom broju konobara, kada dolaze gosti (zadaci), raspoređuju se slobodni konobari (niti) za obradu, čime se izbegava često zapošljavanje i otpuštanje.
Koji je red zadataka u bazenu niti?
Blokirajući red.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za prolećnu regrutaciju Xiaomi-a saputnika K: Recite zašto koristite bazen niti u projektu, važni parametri, dajte primer kako se ti parametri menjaju
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinsku praksu ByteDance-a saputnika 7: Recite zašto uvodimo bazen niti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinski razvoj WeBank-a saputnika 1: Recite svoje razumevanje bazena niti
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog letnjeg praktičnog intervjua Ctrip-a saputnika 10: Recite svoje razumevanje bazena niti i koje scenarije korišćenja
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua za zadnje krilo Ali Mama-a saputnika 5: Recite o konkurentnom sistemu Java-e (od pesimističkih brava do optimističkih, kao i niti, bazeni niti i sl., pričali smo skoro deset minuta o ovome)
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinski razvoj za praksu Wenxin Yiyan 25 Baidu-a saputnika 1: Kako Java kreira niti? Svaki put kada trebate da kreirate novu nit za asinhronu operaciju, veoma je zamorno, da li poznajete bazen niti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua Dewu-a saputnika 9: Recite o bazenu niti? Zašto koristiti bazen niti?
54.Da li ste koristili bazen niti u projektu?
Preporučeno za čitanje: Primena bazena niti u poslovanju Meituan-a
Da, koristio sam mnogo puta.
Na primer utehničko-praktičnom projektu, enkapsulirali smo asinhroni alatski aš AsyncUtil, ugrađeni konfigurabilni bazen niti, baziran na ThreadPoolExecutor-u, pogodan za zadatke intenzivne po U/I.

Gde je corePoolSize dvostruki broj CPU jezgara, jer su većina zadataka u tehničko-praksi intenzivni po U/I, maxPoolSize je postavljeno na 50, što je prilično idealna vrednost, posebno u lokalnom okruženju; blokirajući red je SynchronousQueue, što znači da nakon što se zadatak kreira može direktno predati čekajućoj niti za obradu.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog letnjeg praktičnog intervjua Ctrip-a saputnika 10: Recite svoje razumevanje bazena niti i koje scenarije korišćenja
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Meituan-a saputnika 4: Kako koristite višenitnost uobičajeno
55.🌟Možete li reći kako radi bazen niti?
Može se kratko sumirati kao:
Predaja zadatka → izvršenje osnovnih niti → keširanje reda zadataka → izvršenje ne-osnovnih niti → obrada strategije odbijanja.
Prvi korak, bazen niti predaje zadatak kroz submit().
ExecutorService threadPool = Executors.newFixedThreadPool(5);
threadPool.submit(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "obrada poslovanja");
});Drugi korak, bazen niti će prvo kreirati osnovne niti za izvršenje zadatka.
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) {
return;
}
}Treći korak, ako su osnovne niti zauzete, zadatak će biti stavljen u red zadataka.
workQueue.offer(task);Četvrti korak, ako je red zadataka pun, a trenutni broj niti je manji od maksimalnog broja niti, bazen niti će kreirati nove niti za obradu zadatka.
if (!addWorker(command, false))Peti korak, ako je broj niti u bazenu niti već dostigao maksimalni broj niti, a red zadataka je pun, bazen niti će izvršiti strategiju odbijanja.
handler.rejectedExecution(command, this);Još jedna verzija odgovora.
Prvi korak, kreiranje bazena niti.
Drugi korak, poziva se metod execute() bazena niti, priprema za izvršenje zadatka.
- Ako je broj tekućih niti manji od corePoolSize, bazen niti će kreirati novu nit za izvršenje ovog zadatka;
- Ako je broj tekućih niti veći ili jednak corePoolSize, bazen niti će staviti ovaj zadatak u red čekanja;
- Ako je red čekanja pun, a broj tekućih niti je manji od maximumPoolSize, bazen niti će kreirati nove niti za izvršenje ovog zadatka;
- Ako je red čekanja pun, a broj tekućih niti je veći ili jednak maximumPoolSize, bazen niti će izvršiti strategiju odbijanja.

Treći korak, nakon što nit završi izvršenje, nit neće odmah biti uništena, već će ostati u bazu i čekati sledeći zadatak.
Četvrti korak, kada slobodno vreme niti pređe određeno vreme, a trenutni broj niti je veći od broja osnovnih niti, nit će biti reciklirana.
Možete li dati primer iz stvarnog života?
Može. Postoji banka pod nazivom "Sigurno ćeš postati bogat", ova banka ima 6 šaltera, sada je otvoreno 3 šaltera, na njima sede 3 dame i obrađuju poslovenja.
Lepi momak Xiao Er dolazi da obradi poslovanje, šta će se desiti?
Prva situacija, Xiao Er primećuje da je jedna dama slobodna i čeka, tako da Xiao Er brzo trči da obradi poslovanje.

Druga situacija, damama su sve zauzete, recepcionerka Xiao Mei pozdravlja Xiao Er-a da uzme broj i čeka u redu, kaže mu da bude strpljiv.

Treća situacija, ne samo da su damama sve zauzete, već je i red za čekanje pun, Xiao Er hitno treba novac, tako da se nervno ponaša, sukobljava se sa recepcionerkom Xiao Mei, zahteva da se otvore ostala 3 prazna šaltera.
Xiao Mei pod pritiskom Xiao Er-a, otvara ostala 3 šaltera, ljudi iz reda za čekanje odmah trče tuda.

Četvrta situacija, damame na 6 šaltera su sve zauzete, red za čekanje je takođe pun...

Recepcionerka Xiao Mei daje Xiao Er-u 4 opcije:
- Izvinite, naš sistem "Sigurno ćeš postati bogat" je pauziran.
- Niste videli da smo zauzeti, ko vas je nazvao da biste došli, tražite od toga!
- Lepi momak, vidim da ste hitni, tajno uđite u red.
- Izvinite, danas ne možemo, dođite drugi put.
Ovaj proces ne može biti istovetan bazenu niti, zapravo je istovetan:
- corePoolSize odgovara broju šaltera 3
- maximumPoolSize odgovara maksimalnom broju šaltera 6
- workQueue odgovara redu za čekanje
- handler odgovara recepcionerki Xiao Mei
class ThreadPoolDemo {
public static void main(String[] args) {
// kreiranje bazena niti
ExecutorService threadPool = new ThreadPoolExecutor(
3, // broj osnovnih niti
6, // maksimalni broj niti
0, // slobodno vreme niti
TimeUnit.SECONDS, // vremenska jedinica
new LinkedBlockingQueue<>(10), // red čekanja
Executors.defaultThreadFactory(), // fabrika niti
new ThreadPoolExecutor.AbortPolicy() // strategija odbijanja
);
// simulacija 10 mušterija koji dolaze u banku da obrade poslovanje
try {
for (int i = 1; i <= 10; i++) {
final int tempInt = i;
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t" + "obrada poslovanja" + tempInt);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog letnjeg praktičnog intervjua Meituan-a saputnika 16: Ključni parametri bazena niti, model rada bazena niti
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua odeljenja glavne stanice Kuaishou-a saputnika 1: Proces predaje zadatka bazenu niti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua China Merchants Bank-a saputnika 6: Kada se javlja strategija odbijanja ThreadPoolExecutor-a u JUC konkurentnom programiranju?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua Jingdong-a saputnika 9: Kako radi princip bazena niti?
🌟56.Koji su glavni parametri bazena niti?
Bazen niti ima 7 parametara, treba posebno obratiti pažnju na broj osnovnih niti, maksimalni broj niti, red čekanja, strategiju odbijanja.

①,corePoolSize:broj osnovnih niti, dugotrajno živi, glavni izvršilac zadataka.
②,maximumPoolSize:maksimalni broj niti koji bazen niti dozvoljava.
③,workQueue:red zadataka, čuva zadatke koji čekaju na izvršenje.
④,handler:strategija odbijanja, način obrade kada su zadaci preko kapaciteta. To je kada broj niti dostigne maximumPoolSize, a red zadataka je takođe pun, pokreće se strategija odbijanja.
⑤,threadFactory:fabrika niti, koristi se za kreiranje niti, može se prilagoditi ime niti.
⑥,keepAliveTime:vreme života ne-osnovnih niti, ako slobodno vreme pređe ovu vrednost, uništava se.
⑦,unit:vremenska jedinica parametra keepAliveTime:
- TimeUnit.DAYS; dan
- TimeUnit.HOURS; sat
- TimeUnit.MINUTES; minut
- TimeUnit.SECONDS; sekunda
- TimeUnit.MILLISECONDS; milisekunda
- TimeUnit.MICROSECONDS; mikrosekunda
- TimeUnit.NANOSECONDS; nanosekunda
Možete li kratko reći koja je veza između parametara?
Jednom rečju: zadaci se prvo izvršavaju kroz osnovne niti, kada su pun idu u red čekanja, kada je red pun aktiviraju se ne-osnovne niti kao rezerva, kada bazen niti dostigne maksimalni broj niti pokreće se strategija odbijanja, ne-osnovne niti se recikliraju nakon što njihovo slobodno vreme pređe vreme života.
Šta ako nema dovoljno osnovnih niti?
Kada broj predatih zadataka premaši corePoolSize, ali je manji od maximumPoolSize, bazen niti će kreirati nove niti za obradu zadataka.
Kada broj predatih zadataka premaši maximumPoolSize, bazen niti će obrađivati zadatke prema strategiji odbijanja.
Dajte primer kako se ti parametri menjaju?
Pretpostavimo scenario, konfiguracija bazena niti je sledeća:
corePoolSize = 5
maximumPoolSize = 10
keepAliveTime = 60 sekundi
workQueue = LinkedBlockingQueue (kapacitet 100)
handler = ThreadPoolExecutor.AbortPolicy()Scenario jedan:kada se sistem pokrene, 10 zadataka se preda bazenu niti.
- Prvih 5 zadataka će odmah biti izvršeno, jer ima dovoljno osnovnih niti.
- Sledećih 5 zadataka će biti stavljeno u red čekanja.
Scenario dva:ako se u ovom trenutku još 100 zadataka preda bazenu niti.
- Radni red je pun, bazen niti će kreirati dodatne niti za izvršenje ovih zadataka, dok ukupan broj niti ne dostigne 10.
- Ako zadaci nastave da se povećavaju, i pređu ograničenje radnog reda + maksimalnog broja niti, novi zadaci će biti odbijeni od strane AbortPolicy-a, bacajući RejectedExecutionException izuzetak.
Scenario tri:ako zadaci iznenada smanje:
Osnovne niti će nastaviti da rade, a niti izvan broja osnovnih niti će biti reciklirane nakon 60 sekundi.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za prolećnu regrutaciju Xiaomi-a saputnika K: Recite zašto koristite bazen niti u projektu, važni parametri, dajte primer kako se ti parametri menjaju
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog letnjeg praktičnog intervjua Meituan-a saputnika 16: Ključni parametri bazena niti, model rada bazena niti
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Huawei OD-a saputnika 1: Koji su ključni parametri kreiranja bazena niti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinski razvoj Agricultural Bank of China-e saputnika 3: Recite nekoliko važnih parametara bazena niti
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua odeljenja glavne stanice Kuaishou-a saputnika 1: Koja je razlika između osnovnih i maksimalnih niti? Da li se osnovne niti mogu uništiti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Meituan-a saputnika 4: Šta ako nema dovoljno osnovnih niti?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinski razvoj jesenske regrutacije saputnika 8: Koji bazeni niti postoje i koji su ključni parametri?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua Jingdong-a saputnika 9: Kada se će izvršiti strategija odbijanja?
57.🌟Koje su strategije odbijanja bazena niti?
Postoje četiri:
- AbortPolicy:podrazumevana strategija odbijanja, će baciti RejectedExecutionException izuzetak.
- CallerRunsPolicy:neka nit koja predaje zadatak sama izvrši ovaj zadatak, to jest nit koja poziva execute metod.
- DiscardOldestPolicy:red čekanja će odbaciti najstariji zadatak u redu, to jest zadatak koji najduže čeka u redu, zatim pokušava ponovo da preda odbačeni zadatak.
- DiscardPolicy:odbacuje odbačeni zadatak, ne radi ništa i ne baca izuzetak.

Respektivno odgovara četiri scenarija u kojima Xiao Er dolazi u banku da obradi poslovanje i ga "prati" menadžer: "naš sistem je pauziran", "ko vas je nazvao da biste došli, tražite od toga", "vidim da ste hitni, tajno uđite u red", "danas ne možemo, ne možete li doći drugi dan".
Kada bazen niti ne može da primi nove zadatke, to jest kada broj niti dostigne maximumPoolSize, a red zadataka je takođe pun, pokreće se strategija odbijanja.
Ako podrazumevana strategija ne može da zadovolji potrebe, možete definisati svoju strategiju eliminacije implementacijom interfejsa RejectedExecutionHandler. Na primer: beleženje logova odbačenih zadataka.
class CustomRejectedHandler {
public static void main(String[] args) {
// prilagođena strategija odbijanja
RejectedExecutionHandler rejectedHandler = (r, executor) -> {
System.out.println("Task " + r.toString() + " rejected. Queue size: "
+ executor.getQueue().size());
};
// prilagođeni bazen niti
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // broj osnovnih niti
4, // maksimalni broj niti
10, // vreme života slobodnih niti
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2), // kapacitet blokirajućeg reda
Executors.defaultThreadFactory(),
rejectedHandler // prilagođena strategija odbijanja
);
for (int i = 0; i < 10; i++) {
final int taskNumber = i;
executor.execute(() -> {
System.out.println("Executing task " + taskNumber);
try {
Thread.sleep(1000); // simulacija trajanja zadatka
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
- Vodič za intervjue za Java (plaćeno) sadrži pitanje drugog tehničkog intervjua DiDi-ja saputnika 2: Recite o strategijama odbijanja u konkurentnom programiranju, koje situacije odgovaraju kojoj strategiji odbijanja
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua Meituan-a saputnika 3: Kako dizajnirati bazen niti, koje su strategije odbijanja, kako ih birati
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Meituan-a saputnika 4: Koje strategije zasićenja postoje
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Li Xiang Qi Che-a saputnika 2: Strategije eliminacije bazena niti, dodatno pitanje: da li se može prilagoditi strategija eliminacije? Koja je implementaciona klasa strategije eliminacije?
58.Koje blokirajuće redove postoje u bazenu niti?
Često korišćenih pet, ograničeni red ArrayBlockingQueue; neograničeni red LinkedBlockingQueue; red prioriteta PriorityBlockingQueue; odloženi red DelayQueue; sinhroni red SynchronousQueue.

①,ArrayBlockingQueue:ograničeni FIFO blokirajući red, na nižem nivou je niz, pogodan za bazen niti fiksne veličine.
ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10, true);②,LinkedBlockingQueue:na nižem nivou je lista, ako se ne navede veličina, podrazumevana veličina je Integer.MAX_VALUE, skoro ekvivalentno neograničenom redu.
Utehničko-praktičnom projektu, koristili smo LinkedBlockingQueue za konfigurisanje RabbitMQ reda poruka.

③,PriorityBlockingQueue:neograničeni blokirajući red koji podržava sortiranje po prioritetu. Zadaci se sortiraju prema njihovom prirodnom redu ili Comparator-u.
Pogodno za scenarije koji zahtevaju obradu zadataka prema datom prioritetu, na primer prvo obraditi hitne zadatke.
④,DelayQueue:slično kao PriorityBlockingQueue, neograničeni blokirajući red prioriteta implementiran binarnom gomilom.
Executors-ova newScheduledThreadPool() koristi DelayQueue za ostvarivanje odloženog izvršenja.
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}⑤,SynchronousQueue:svaka operacija umetanja mora čekati drugu nit da ukloni element, takođe svaka operacija uklanjanja mora čekati drugu nit da umetne element.
Executors.newCachedThreadPool() koristi SynchronousQueue, ovaj bazen niti će kreirati nove niti prema potrebi, ako ima slobodnih niti će ih ponovo koristiti, nit se reciklira nakon 60 sekundi slobodnog vremena.
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinski razvoj WeBank-a saputnika 1: Koje implementacione metode blokirajućeg reda bazena niti postoje?
memo: 4. septembar 2025. izmenjeno do ovde, danas kada sam pomagao saputniku da izmeni rezime, saputnik je primio povratnu informaciju da je zahvaljujućirezimeu koji je Erge izmenio dobio praksu u ByteDance-u, sada je jesen regrutacije, nadam se da može dalje optimizovati rezime.

59.Koja je razlika između submit i execute bazena niti?
execute metod nema povratnu vrednost, pogodan za jednostavne zadatke koji ne brinu o rezultatu i izuzetku.
threadsPool.execute(new Runnable() {
@Override public void run() {
System.out.println("zadatak predat metodom execute()");
}
});submit ima povratnu vrednost, pogodan za scenarije gde je potrebno dobiti rezultat ili obraditi izuzetak.
Future<Object> future = executor.submit(harReturnValuetask);
try { Object s = future.get(); }
catch (InterruptedException e | ExecutionException e) {
// obrada izuzetka nemogućnosti izvršenja zadatka
} finally {
// zatvaranje bazena niti executor.shutdown();
}60.Da li znate kako se gasi bazen niti?
Možete pozvati metod shutdown ili shutdownNow bazena niti da biste gasili bazen niti.
shutdown neće odmah zaustaviti bazen niti, već će čekati da se svi zadaci završe pa onda gasiti bazen niti.
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(() -> System.out.println("Task 1"));
executor.execute(() -> System.out.println("Task 2"));
executor.shutdown(); // neće odmah gasiti, već će čekati da se svi zadaci završeshutdownNow će pokušati da zaustavi bazen niti kroz niz akcija, uključujući prestanak prihvatanja spoljnih predatih zadataka, ignorisanje zadataka koji čekaju u redu, pokušaj da se prekinu tekući zadaci interrupt-om.
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(() -> {
try {
Thread.sleep(5000); // simulacija zadatka dugog trajanja
System.out.println("Task executed");
} catch (InterruptedException e) {
System.out.println("zadatak je prekinut");
}
});
List<Runnable> unexecutedTasks = executor.shutdownNow(); // odmah gasi bazen niti
System.out.println("Broj neizvršenih zadataka: " + unexecutedTasks.size());Treba napomenuti da shutdownNow neće zaista zaustaviti zadatke koji se trenutno izvršavaju, već će samo poslati interrupt signal nitima zadataka, da li zadatak može zaista biti zaustavljen zavisi od toga da li nit odgovara na InterruptedException.
61.Kako treba konfigurisati broj niti u bazenu niti?
Prvo, analiziraću tip zadataka koji se izvršavaju u bazenu niti, da li su CPU intenzivni ili IO intenzivni?
①,Za CPU intenzivne zadatke, moj cilj je da što više smanjim kontekst prekide niti, kako bih optimizovao iskorišćenje CPU-a. Generalno, postavljanje broja osnovnih niti na broj jezgara procesora ili broj jezgara + 1 je prilično idealan izbor.
+1 je za rezervne slučajeve, ako se niti blokiraju zbog čekanja na sistemske resurse, može imati više niti da ih zamene, tako da ne utiče na celokupnu performansu.
②,Za IO intenzivne zadatke, pošto niti često staju u stanje čekanja, čekajući na završetak IO operacija, može se postaviti više niti za poboljšanje konkurentnosti, na primer dvostruki broj CPU jezgara.

Broj jezgara može se dobiti metodom Java-e
Runtime.getRuntime().availableProcessors().
Konačno, ću prilagoditi druge parametre bazena niti prema poslovnim potrebama i sistemskim resursima, na primer maksimalni broj niti, kapacitet reda zadataka, vreme života slobodnih ne-osnovnih niti i dr.
ThreadPoolExecutor executor = new ThreadPoolExecutor(
cores, // broj osnovnih niti postavljen na broj CPU jezgara
cores * 2, // maksimalni broj niti je dvostruki broj jezgara
60L, TimeUnit.SECONDS, // vreme života slobodnih ne-osnovnih niti
new LinkedBlockingQueue<>(100) // kapacitet reda zadataka
);Kako znate da li ste postavili previše ili premalo niti?
Možete proceniti da li je broj niti preveliki ili premali kroz monitoring i debagovanje.
Na primer kroz top komandu posmatrati stopu iskorišćenja CPU-a, ako je stopa iskorišćenja CPU-a niska, možda je broj niti premali; ako je stopa iskorišćenja CPU-a blizu 100%, ali se propusnost ne poboljšava, možda je broj niti preveliki.
Zatim kroz VisualVM ili Arthas analizirati stanje pokretanja niti, pregledati stanje niti, vreme čekanja, vreme izvršenja i druge informacije.
Takođe možete koristiti jstack komandu da pregledate informacije o steku niti, da li su niti u stanju blokade.
jstack <Java proces ID> | grep -A 20 "BLOCKED" // pregled blokiranih nitiAko ima puno BLOCKED niti, to ukazuje da je možda broj niti preveliki, konkurencija je prilično intenzivna.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinsku praksu ByteDance-a saputnika 7: Kako planirate broj osnovnih niti bazena niti, kako razmatrate proces?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje drugog intervjua Bilibili-ja saputnika 1: Recite o svom razumevanju raznih parametara bazena niti; kako znate da li ste postavili previše ili premalo niti?
62.Koji često korišćeni bazeni niti postoje?
Glavno postoje četiri vrste:
Bazen niti fiksne veličine Executors.newFixedThreadPool(int nThreads);, pogodan za scenarije gde je broj zadataka jasno definisan i postoje jasni zahtevi za broj niti. Na primer, IO intenzivni zadaci, baza podataka za povezivanje i sl.
Keš bazen niti Executors.newCachedThreadPool();, pogodan za scenarije gde se broj zadataka drastično menja u kratkom vremenu. Na primer, velik broj zadataka za obradu datoteka ili mrežnih zahteva u kratkom vremenu.
Bazen niti za vremenske zadatke Executors.newScheduledThreadPool(int corePoolSize);, pogodan za scenarije gde treba periodički izvršavati zadatke. Na primer, periodično slanje imejlova, periodično pravljenje rezerve podataka i dr.
Bazen niti sa jednom niti Executors.newSingleThreadExecutor();, pogodan za scenarije gde treba zadatke izvršavati po redu. Na primer, logiranje, obrada datoteka i dr.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje intervjua BYD-a saputnika 1: Da li ste koristili bazen niti, koji bazeni niti postoje?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua za pozadinski razvoj jesenske regrutacije OPPO-a saputnika 8: Koji bazeni niti postoje i koji su ključni parametri?
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog intervjua Li Xiang Qi Che-a saputnika 2: Koji bazeni niti postoje u JAVA-i?
memo: 17. avgust 2025. izmenjeno do ovde.danas sam primio povratnu informaciju saputnika, slučajno se pridružio planeti, nakon učenja oseća da je prilično dobro, hvala saputniku za svaku preporuku, srce.

63.Možete li reći princip implementacije četiri često korišćena bazena niti?
Bilo da je FixedThreadPool, CachedThreadPool, ili SingleThreadExecutor i ScheduledThreadPoolExecutor, suštinski su samo različite konfiguracije ThreadPoolExecutor-a.
Kako radi bazen niti fiksne veličine?
Veličina bazena niti je fiksirana, corePoolSize == maximumPoolSize, podrazumevano koristi LinkedBlockingQueue kao blokirajući red, pogodan za scenarije sa stabilnim brojem zadataka, kao što su baza podataka za povezivanje, RPC obrada i dr.
new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());Kada se preda novi zadatak, ako bazen niti ima slobodnih niti, direktno se izvršava; ako ne, zadatak ulazi u LinkedBlockingQueue i čeka. Mana je što red zadataka podrazumevano nema granice, može doći do nakupljanja zadataka, čak i do OOM.

Kako radi keš bazen niti?
Veličina bazena niti nije fiksirana, corePoolSize = 0, maximumPoolSize = Integer.MAX_VALUE. Slobodne niti će biti uništene nakon više od 60 sekundi, koristi SynchronousQueue kao blokirajući red, pogodan za scenarije gde u kratkom vremenu ima puno zadataka.
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<>());Kada se preda zadatak, ako bazen niti nema slobodnih niti, direktno se kreira nova nit za izvršenje zadatka; ako ima, ponovo se koristi nit za izvršenje zadatka. Nit se reciklira nakon 60 sekundi slobodnog vremena, smanjujući zauzeće resursa. Mana je što broj niti nema gornju granicu, u visoko konkurentnom okruženju može doći do OOM.

Kako radi bazen niti sa jednom niti?
Bazen niti ima samo 1 nit, garantuje da se zadaci izvršavaju po redu predaje, koristi LinkedBlockingQueue kao blokirajući red, pogodan za scenarije gde treba zadatke izvršavati po redu.
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());Uvek kreira samo 1 nit, novi zadatak mora čekati da se prethodni završi pre nego što može da se izvrši, ostali zadaci se stavljaju u LinkedBlockingQueue i čekaju izvršenje po redu. Mana je što ne može paralelno obrađivati zadatke.

Kako radi bazen niti za vremenske zadatke?
Veličina bazena niti za vremenske zadatke je konfigurabilna, podržava izvršavanje zadataka po vremenu & periodično, koristi DelayedWorkQueue kao blokirajući red, pogodan za scenarije periodičkog izvršenja zadataka.
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}Kada se izvršavaju vremenski zadaci, metod schedule() može odložiti izvršenje zadatka za određeno vreme i izvršiti ga jednom; metod scheduleAtFixedRate() može odložiti izvršenje zadatka za određeno vreme i izvršiti ga fiksnom frekvencijom; metod scheduleWithFixedDelay() može odložiti izvršenje zadatka za određeno vreme i izvršiti ga fiksnim kašnjenjem.

Mana je što ako vreme izvršenja zadatka > od postavljenog vremenskog intervala, scheduleAtFixedRate može dovesti do nakupljanja zadataka.

Koji problemi će se pojaviti ako bazen niti koristi neograničeni red?
Ako nit nakon što dobije zadatak, vreme izvršenja zadatka je prilično dugo, to će dovesti do toga da se zadaci u redu sve više nakupljaju, korišćenje memorije će se neprestano povećavati, najzad će doći do OOM.
64.Da li znate kako se obrađuju izuzeci bazena niti?
Česti načini obrade su korišćenje try-catch za hvatanje, korišćenje Future za dobijanje izuzetka, prilagođeni ThreadPoolExecutor preuzimanjem afterExecute metoda, korišćenje UncaughtExceptionHandler za hvatanje izuzetka.

①,try-catch je najjednostavniji metod.
executor.execute(() -> {
try {
System.out.println("početak zadatka");
int result = 1 / 0; // izuzetak deljenja nulom
} catch (Exception e) {
System.err.println("uhvaćen izuzetak:" + e.getMessage());
}
});②,Koristiti Future za dobijanje izuzetka.
Future<Object> future = executor.submit(() -> {
System.out.println("početak zadatka");
int result = 1 / 0; // izuzetak deljenja nulom
return result;
});
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
System.err.println("uhvaćen izuzetak:" + e.getMessage());
}③,Prilagođeni ThreadPoolExecutor preuzimanjem afterExecute metoda.
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
System.err.println("uhvaćen izuzetak:" + t.getMessage());
}
}
};
executor.execute(() -> {
System.out.println("početak zadatka");
int result = 1 / 0; // izuzetak deljenja nulom
});④,Koristiti UncaughtExceptionHandler za hvatanje izuzetka.
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.setThreadFactory(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("uhvaćen izuzetak:" + e.getMessage());
}
});
return thread;
}
});
executor.execute(() -> {
System.out.println("početak zadatka");
int result = 1 / 0; // izuzetak deljenja nulom
});Ako projekat koristi execute(), ne brine o povratnoj vrednosti zadatka, preporučuje se korišćenje UncaughtExceptionHandler:
thread.setUncaughtExceptionHandler((t, e) ->
System.err.println("nit " + t.getName() + " uhvatila izuzetak:" + e.getMessage()));Ako projekat koristi submit(), brine o povratnoj vrednosti zadatka, preporučuje se korišćenje Future:
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
System.err.println("uhvaćen izuzetak:" + e.getCause());
}Ako želite da globalno hvata sve izuzetke zadataka, preporučuje se preuzimanje afterExecute metoda:
class MyThreadPoolExecutor extends ThreadPoolExecutor {
@Override
protected void afterExecute(Runnable r, Throwable t) {
if (t == null && r instanceof Future<?>) {
try { ((Future<?>) r).get(); } catch (Exception e) { System.err.println("izuzetak zadatka:" + e.getCause()); }
}
}
}65.Možete li reći koja stanja ima bazen niti?
Postoji 5 stanja, njihova konverzija sledi stroga pravila prelaza stanja, različita stanja kontrolišu raspored zadataka i ponašanje gašenja bazena niti.
Stanja se prelaze od RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED redom.

RUNNING stanje bazena niti može primati nove zadatke i obrađivati zadatke u blokirajućem redu; SHUTDOWN stanje bazena niti neće primati nove zadatke, ali će obrađivati zadatke u blokirajućem redu; STOP stanje bazena niti neće primati nove zadatke, neće obrađivati zadatke u blokirajućem redu i će pokušati da prekine tekuće zadatke; TIDYING stanje označava da su svi zadaci već završeni; TERMINATED stanje označava da je bazen niti potpuno gasen, sve niti su uništene.
| Stanje | Kôd stanja | Da li prima nove zadatke | Da li izvršava zadatke u redu | Da li prekida tekuće zadatke |
|---|---|---|---|---|
| RUNNING | 111 | ✅ Da | ✅ Da | ❌ Ne |
| SHUTDOWN | 000 | ❌ Ne | ✅ Da | ❌ Ne |
| STOP | 001 | ❌ Ne | ❌ Ne | ✅ Da |
| TIDYING | 010 | ❌ Ne | ❌ Ne | ❌ Ne |
| TERMINATED | 011 | ❌ Ne | ❌ Ne | ❌ Ne |
memo: 24. februar 2025. izmenjeno do ovde. Danas je dan kada se objavljuju rezultati ispita za postdiplomske studije, nadam se da će svi kandidati koji su učestvovali u ispitu postići idealne rezultate.

66.Kako bazen niti ostvaruje dinamičku izmenu parametara?
Bazen niti pruža setter metode koji mogu dinamički menjati parametre u vreme izvršenja, na primer setCorePoolSize se može koristiti za izmenu broja osnovnih niti, setMaximumPoolSize se može koristiti za izmenu maksimalnog broja niti.

Treba napomenuti da kada se pozove setCorePoolSize(), ako je novi broj osnovnih niti veći od prethodnog, bazen niti će kreirati nove niti; ako je manji, bazen niti neće odmah uništiti višak niti, osim ako nema slobodnih niti koje premašile keepAliveTime.
Naravno, takođe možete koristiti Nacos konfiguracioni centar, ili implementirati prilagođeni bazen niti, pratiti promene parametara za dinamičko prilagođavanje parametara.

67.🌟Da li poznajete optimizaciju bazena niti? (dopuna)
- mart 2024. dodato, preporučeno za čitanje: Implementacija principa bazena niti u Java-i i njegova praksa u poslovanju Meituan-a
Optimizacija konfiguracije bazena niti je ključni deo poboljšanja performansi višenitnih aplikacija.

Prvo ću postaviti parametre broja osnovnih niti prema tipu zadatka, na primer za IO intenzivne zadatke ću postaviti na CPU jezgara*2 kao praktično iskustvo.
Drugo, ću kombinovati sposobnost dinamičkog prilagođavanja bazena niti, u vreme fluktuacije saobraćaja kroz setCorePoolSize sporo proširiti, ili direktno koristiti DynamicTp za ostvarivanje automatskog prilagođavanja parametara bazena niti.
Konačno, ću uspostaviti mehanizam upozorenja kapaciteta kroz ugrađene monitoring indikatore. Na primer kroz JMX pratiti stanje rada bazena niti, postaviti prag, kada dužina reda zadataka bazena niti premaši prag, pokrenuti upozorenje.
memo: 26. septembar 2025. optimizovano do ovde, danas je saputnik u VIP grupi rekao, nakon samo 1 dana kandidacije, dobio je intervju,rezime koji je Erge promenio je previše dobar. Vrlo sam zahvalan na njegovom priznanju.

68.Na šta treba obratiti pažnju pri korišćenju bazena niti? (dopuna)
- mart 2024. dodato
Mislim da postoji 3 važna fokusa:
Prvi, izbor pogodne veličine bazena niti. Previše mali bazen niti može dovesti do toga da zadaci stalno čekaju; Previše veliki bazen niti može dovesti do toga da svi konkurišu za CPU resurse, povećavajući troškove kontekstnih prekida
Drugi, izbor pogodnog reda zadataka. Korišćenje ograničenog reda može izbeći rizik od iscrpljivanja resursa, ali može dovesti do odbijanja zadataka; korišćenje neograničenog reda može izbeći odbijanje zadataka, ali može dovesti do iscrpljivanja memorije
Na primer kada koristite LinkedBlockingQueue, možete proslediti parametar da biste ograničili broj zadataka u redu, tako da neće doći do OOM.
Treći, pokušajte da koristite prilagođeni bazen niti, a ne bazen niti kreiran sa Executors-om.
Jer newFixedThreadPool bazen niti zbog korišćenja LinkedBlockingQueue, kapacitet reda je podrazumevano neograničen, kada je previše zadataka će doći do prekoračenja memorije; newCachedThreadPool bazen niti zbog neograničenog broja osnovnih niti, kada je previše zadataka će kreirati mnogo niti, dovodeći do previsokog opterećenja servera i pausiranja.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje drugog tehničkog intervjua DiDi-ja saputnika 2: Na šta treba obratiti pažnju pri korišćenju bazena niti
memo: 25. februar 2025. izmenjeno do ovde.
69.🌟Možete li dizajnirati i implementirati bazen niti?
Preporučeno za čitanje: Princip bazena niti Sanfen E-a
Glavni cilj bazena niti je da se izbegne često kreiranje i uništavanje niti.

Bazen niti će posmatrati kao fabriku, unutra ima grupa "radnika", to jest niti, specijalizovana za obradu zadataka.
Kada dođe zadatak, prvo treba suditi da li ima slobodnih "radnika", ako ima, predaj im zadatak; ako nema, privremeno stavi zadatak u red zadataka, i nakon što "radnici" završe posao, idite da obradite.
Ako je red pun, a još uvek nema slobodnih "radnika", treba razmotriti proširenje, pustiti rezervne "radnike" da rade, ali ne može preći predviđeni maksimum, kako se fabrika ne bi prepunila.
Ako ni proširenje ne može rešiti, potrebna je strategija odbijanja, moža direktno odbiti zadatak ili prijaviti grešku.
Osnovna klasa bazena niti (za referencu):
class CustomThreadPoolExecutor {
private final int corePoolSize;
private final int maximumPoolSize;
private final long keepAliveTime;
private final TimeUnit unit;
private final BlockingQueue<Runnable> workQueue;
private final RejectedExecutionHandler handler;
private volatile boolean isShutdown = false;
private int currentPoolSize = 0;
// metod konstruktora
public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
this.unit = unit;
this.workQueue = workQueue;
this.handler = handler;
}
// predaja zadatka
public void execute(Runnable task) {
if (isShutdown) {
throw new IllegalStateException("ThreadPool is shutdown");
}
synchronized (this) {
// ako je trenutni broj niti manji od broja osnovnih niti, direktno kreira novu nit
if (currentPoolSize < corePoolSize) {
new Worker(task).start();
currentPoolSize++;
return;
}
// pokušava da doda zadatak u red
if (!workQueue.offer(task)) {
if (currentPoolSize < maximumPoolSize) {
new Worker(task).start();
currentPoolSize++;
} else {
// poziva strategiju odbijanja
handler.rejectedExecution(task, null);
}
}
}
}
// gašenje bazena niti
public void shutdown() {
isShutdown = true;
}
// radnička nit
private class Worker extends Thread {
private Runnable task;
Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
while (task != null || (task = getTask()) != null) {
try {
task.run();
} finally {
task = null;
}
}
}
// dobavljanje zadataka iz reda
private Runnable getTask() {
try {
return workQueue.poll(keepAliveTime, unit);
} catch (InterruptedException e) {
return null;
}
}
}
}Strategija odbijanja:
/**
* Strategija odbijanja
*/
class CustomRejectedExecutionHandler {
// AbortPolicy baca izuzetak
public static class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RuntimeException("Task " + r.toString() + " rejected from " + e.toString());
}
}
// DiscardPolicy ne radi ništa
public static class DiscardPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// Do nothing
}
}
// DiscardOldestPolicy odbacuje najstariji zadatak u redu
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
}Primer korišćenja:
class ThreadPoolTest {
public static void main(String[] args) {
// kreiranje bazena niti
CustomThreadPoolExecutor executor = new CustomThreadPoolExecutor(
2, 4, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2),
new CustomRejectedExecutionHandler.AbortPolicy());
// predaja zadataka
for (int i = 0; i < 10; i++) {
final int index = i;
executor.execute(() -> {
System.out.println("Task " + index + " is running");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// gašenje bazena niti
executor.shutdown();
}
}Rezultat izvršenja:

Možete li napisati bazu za povezivanje baza podataka?
Može, moja ideja je ovakva: baza za povezivanje baza podataka je uglavnom da bi se izbeglo kreiranje veza pri svakom operisanju bazom podataka, jer to veoma troši resurse. Zato namjeravam unapred kreirati fiksni broj veza pri inicijalizaciji, zatim ih staviti u siguran kontejner za niti, naknadno kada ima zahteva, uzimati iz reda, a nakon korišćenja vraćati nazad u red.
class SimpleConnectionPool {
// konfiguracija
private String jdbcUrl;
private String username;
private String password;
private int maxConnections;
private BlockingQueue<Connection> connectionPool;
// metod konstruktora
public SimpleConnectionPool(String jdbcUrl, String username, String password, int maxConnections) throws SQLException {
this.jdbcUrl = jdbcUrl;
this.username = username;
this.password = password;
this.maxConnections = maxConnections;
this.connectionPool = new LinkedBlockingQueue<>(maxConnections);
// inicijalizacija baze za povezivanje
for (int i = 0; i < maxConnections; i++) {
connectionPool.add(createNewConnection());
}
}
// kreiranje nove veze
private Connection createNewConnection() throws SQLException {
return DriverManager.getConnection(jdbcUrl, username, password);
}
// dobavljanje veze
public Connection getConnection(long timeout, TimeUnit unit) throws InterruptedException, SQLException {
Connection connection = connectionPool.poll(timeout, unit); // čekanje na dobavljanje veze određeno vreme
if (connection == null) {
throw new SQLException("Timeout: Unable to acquire a connection.");
}
return connection;
}
// vraćanje veze
public void releaseConnection(Connection connection) throws SQLException {
if (connection != null) {
if (connection.isClosed()) {
// ako je veza zatvorena, kreira novu vezu za dopunu u bazu
connectionPool.add(createNewConnection());
} else {
// vraćanje veze u bazu
connectionPool.offer(connection);
}
}
}
// zatvaranje svih veza
public void closeAllConnections() throws SQLException {
for (Connection connection : connectionPool) {
if (!connection.isClosed()) {
connection.close();
}
}
}
// test slučaj
public static void main(String[] args) {
try {
SimpleConnectionPool pool = new SimpleConnectionPool(
"jdbc:mysql://localhost:3306/pai_coding", "root", "", 5
);
// dobavljanje veze
Connection conn = pool.getConnection(5, TimeUnit.SECONDS);
// korišćenje veze (primer upita)
System.out.println("Connection acquired: " + conn);
Thread.sleep(2000); // simulacija upita
// vraćanje veze
pool.releaseConnection(conn);
System.out.println("Connection returned.");
// zatvaranje svih veza
pool.closeAllConnections();
} catch (Exception e) {
e.printStackTrace();
}
}
}Rezultat izvršenja:

- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua Meituan-a saputnika 3: Kako dizajnirati bazen niti, koje su strategije odbijanja, kako ih birati
- Vodič za intervjue za Java (plaćeno) sadrži pitanje drugog intervjua Bilibili-ja saputnika 1: Dajem vam zahtev, možete sada napisati bazu za povezivanje
70.Kako trebaračiti ako bazen niti ostane bez struje tokom izvršenja?
Bazen niti sam može samo rasporediti zadatke u memoriji, ne vrši perzistenciju, jednom kada ostane bez struje, svi zadaci i stanja u bazenu niti će biti izgubljena.
Razmotriti ću sledeće aspekte:
Prvo, perzistencija zadataka. Zadaci se mogu perzistirati u bazu podataka ili red poruka, nakon što se struja vrati ponovo ih izvršiti.
Drugo, idempotentnost zadatka, treba osigurati da su zadaci idempotentni, to jest bez obzira koliko puta se izvrše, rezultat je isti.
Treće, strategija oporavka. Kada se sistem ponovo pokrene, treba postojati proces oporavka: otkriti da li je prošlog puta bilo nezavršenih zadataka, ponovo učitati te zadatke u bazen niti za izvršenje, osiguravajući da se može nastaviti posao pre isključenja struje.
Konkurentni kontejneri i okviri
71.Koji konkurentni kontejneri postoje?
Java pruža više konkurentnih kontejnera, uglavnom uključuju:
- ConcurrentHashMap:heš tabela sigurna za niti, podržava visoko konkurentno čitanje i pisanje.
- CopyOnWriteArrayList:pisati pri kopiranju ArrayList, pogodan za scenarije sa mnogo čitanja i malo pisanja.
- BlockingQueue:blokirajući red, česte implementacije su ArrayBlockingQueue i LinkedBlockingQueue, pogodan za scenarije proizvođa-potrošač.
- ConcurrentLinkedQueue:neblokirajući red siguran za niti.
- ConcurrentSkipListMap:konkurentna uređena mapa bazirana na skip list.
- Vodič za intervjue za Java (plaćeno) sadrži pitanje prvog tehničkog intervjua za pozadinski razvoj ByteDance-a saputnika 34: Koji konkurentni kontejneri postoje?
72.Koji su konkurentni ključne reči u Java-i?
Najčešće se koriste dve ključne reči, respektivno:
- synchronized:koristi se za metode ili blokove koda, osigurava da istovremeno samo jedna nit može izvršiti kod modifikovan sa synchronized, pogodan za zaštitu pristupa deljenim resursima.
- volatile:koristi se za promenljive, osigurava vidljivost promenljivih, sprečava preuređivanje instrukcija, pogodan za scenarije poput oznaka stanja.
73.Da li poznajete Fork/Join okvir?
O Fork/Join okviru, znam malo, to je paralelni okvir koji je Java 7 uvela, uglavnom korišćen za paralelno izvršenje algoritama podele. Ovaj okvir rekurzivno deli velike zadatke na više malih zadataka, zatim ih paralelno izvršava, najzad sažima rezultate, kako bi se postigao maksimalni efikasitet za obradu velike količine podataka.

Osnovna ideja Fork/Join okvira je podeli pa vladaj, veliki zadaci se dele na više malih zadataka za paralelnu obradu, zatim se sažimaju rezultati tih malih zadataka.
Ko drvo strukture, korenski čvor je veliki zadatak, list čvorovi su najmanji podzadaci, svaki zadatak može biti podeljen na manje podzadatke, dok se ne dostigne određena kritična tačka, zadaci se onda izvršavaju jedan po jedan.
Konkretno, Fork/Join uključuje dve glavne klase:
ForkJoinPool, specijalan bazen niti, na nižem nivou koristi algoritam za krađu posla, to jest kada jedna nit završi svoje zadatke, može da ukrade zadatke drugih niti, čime se izbegava idle niti.

RecursiveTask i RecursiveAction, respektivno koriste se za zadatke sa i bez povratne vrednosti, obe klase nasleđuju od ForkJoinTask.
class ForkJoinExample {
public static void main(String[] args) {
int[] arr = new int[100];
for (int i = 0; i < 100; i++) {
arr[i] = i + 1; // popunjavanje podataka od 1 do 100
}
// kreiranje ForkJoinPool, podrazumevano koristi dostupan broj procesorskih jezgara
ForkJoinPool pool = new ForkJoinPool();
// kreiranje ForkJoin zadatka
SumTask task = new SumTask(arr, 0, arr.length);
// izvršenje zadatka
Integer result = pool.invoke(task);
System.out.println("Zbir niza je: " + result);
}
// prilagođeni zadatak, nasleđuje RecursiveTask
static class SumTask extends RecursiveTask<Integer> {
private int[] arr;
private int start;
private int end;
public SumTask(int[] arr, int start, int end) {
this.arr = arr;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= 10) { // ako je zadatak dovoljno mali, direktno izračunaj
int sum = 0;
for (int i = start; i < end; i++) {
sum += arr[i];
}
return sum;
} else {
// u suprotnom podeli zadatak
int mid = (start + end) / 2;
SumTask left = new SumTask(arr, start, mid);
SumTask right = new SumTask(arr, mid, end);
// izvrši podzadatke respektivno
left.fork();
right.fork();
// sažmi rezultate
int leftResult = left.join();
int rightResult = right.join();
return leftResult + rightResult; // sažimanje rezultata
}
}
}
}memo: 26. februar 2025. izmenjeno do ovde. Konačno gotovo, "Mijan zang sil" konkurentno programiranje je konačno gotovo, majko mi, bilo je vrlo teško.
Osjećam da 34 hiljada znakova, bar sam promenio 24 hiljade znakova, zaista sam uložio mnogo truda u ovu stvar, nadam se da će pomoći svima u intervjuu.
Pogledajte ovog saputnika, beleženje učešća, drugi intervju za letnju praksu ByteDance-a 2.25, uglavnom su pitanja iz "Mijan zang sil"-a, saputnik je takođe rekao, "Osjećam da intervjueri uopšte neće biti previše teški."
Drukim rečima, dok dobro zapamtite "Mijan zang sil", bazično možete da se nosite sa većinom intervjua.

Konačno su završene druge izdanja poglavlja "Mijan zang sil" konkurentno programiranje, recimo nekoliko reči iz srca.

Na internetu zapravo ima puto "batu" materijala, neki su i plaćeni, mislim da je to dobra stvar, može dati ljudima više izbora, ali kvalitet "Mijan zang sil"-a, oni koji znaju, znaju.

Drugo izdanje "Mijan zang sil"-a je na osnovu početne verzije gostujućeg saradnika Sanfen E, dodato moje razmišljanje Erge, rezultat nakon dodavanja više od 1000 stvarnih intervju zapisa, i od 24. do 25. generacije, pomoglo je mnoge saradnike. Buduće 26, 27. generacija će takođe imati koristi, čime će dobiti željenu ponudu.
Vrlo sam srećan što mogu pomoći svima, i tokom procesa prepravljanja "Mijan zang sil"-a, sam takođe mnogo rastao, mnoge slabe osnovne delove su pojačani, pa drugo izdanje "Mijan zang sil"-a nije samo poklon svima, već i zapis moje tehničke transformacije.



Često osećam da sam filozofska osoba, ne želim da se takmičim sa drugima, niti da namerno promovišem svoja dela.
Volim da čekam cvetanje.
Ako smatrate da "Mijan zang sil" nije loš, možete reći mlađim saradnicima da postoji takav besplatni obrazovni materijal, pomozite mi da stvorimo reputaciju.
Nastaviću da optimizujem, nije sigurno kada će doći treće izdanje, ali ću učiniti sve što mogu.
Neka svi imaju svetlu budućnost.
Ovaj put i dalje tri verzije, svetla, tamna i epub verzija. Prikažimo jednu epub verziju, neki saradnici vrlo hitno trebaju ovu verziju, tako da i njima zadovoljavamo.

Kako PDF ne može automatski da se ažurira, saradnici kojima treba najnovija verzija mogu da pretraže[Chenmo Wang Er] na WeChat-u, ili da skeniraju/pritisnite i prepoznaju dole QR kod, prate Ergov zvanični nalog,odgovorite[222] da biste dobili najnoviju verziju.
Naravno, dozvolite mi mala sebičnost, to jest PDF verzija planete će biti mesec dana ranija od zvaničnog naloga, pošto su korisnici planete već platili, moram im omogućiti da uživaju malo blagodeti malo ranije. Vernem se da svi mogu razumeti,Na kraju, onlajn verzija je besplatna, a CDN, serveri, domeni, OSS itd. svi zahtevaju troškove.
Ne spominjući moje vreme i energiju, ako smatrate da je od koristi, molim vas dajte preporuku, pusti svoje kolege i saradnike da takođe imaju koristi.

stavio sam Ergov put napredovanja Java-e, put napredovanja JVM-a, put napredovanja konkurentnog programiranja, kao i sva izdanja "Mijan zang sil"-a, uključujući Java osnovne, Java zbirke, Java konkurentnost, JVM, Spring, MyBatis, računarne mreže, operativne sisteme, MySQL, Redis, RocketMQ, distribuirane sisteme, mikroservise, dizajn patterne, Linux i dr 16 velikih tema, ukupno više od 400 hiljada znakova, 2000+ ručnih crteža, zaista je puno erfega.
Detaljno objašnjenje 73 čestih pitanja za intervju o konkurentnosti Java-e, ovog intervjua, sigurno ćete "pobediti" intervjuera.
Ništa me ne zadržava — osim cilja, čak i ako na obali ima ruža, zelenila, mirne luke, ja sam brod bez veza.
Serija sadržaja:
- Mijan zang sil Java SE poglavlje 👍
- Mijan zang sil Java zbirke poglavlje 👍
- Mijan zang sil Java konkurentno programiranje poglavlje 👍
- Mijan zang sil JVM poglavlje 👍
- Mijan zang sil Spring poglavlje 👍
- Mijan zang sil Redis poglavlje 👍
- Mijan zang sil MyBatis poglavlje 👍
- Mijan zang sil MySQL poglavlje 👍
- Mijan zang sil operativni sistem poglavlje 👍
- Mijan zang sil računarske mreže poglavlje 👍
- Mijan zang sil RocketMQ poglavlje 👍
- Mijan zang sil distribuirani sistem poglavlje 👍
- Mijan zang sil mikroservisi poglavlje 👍
- Mijan zang sil dizajn patterni poglavlje 👍
- Mijan zang sil Linux poglavlje 👍
- Mijan zang sil OpenClaw poglavlje 👍
- Mijan zang sil Skills poglavlje 👍
