Hirenix kaise padhata hai
Ek chapter. 90 minute.
Interview ke liye taiyaar.
Har concept ek real-world problem se — jaisa production code mein aata hai, waisa. Ratna nahi padta, samajh aa jaata hai. Har question ka model answer diya hai: interviewer ko exactly kya bolna hai, aur kyun. Phir usi chapter ka AI mock interview.
- 📖Concept, 5 min meinJargon nahi — seedhi baat
- 🛠️Real-world problemJaisa production code mein aata hai
- 💬Model answerInterview mein kya bolna hai
- 🧠FlashcardsRevision 10 min mein
- 🤖AI mock interviewFollow-up bhi poochta hai
- 📊Weak topicsKahan phans rahe ho, pata chale

Farq content ka nahi, filter ka hai — sirf wahi jo production mein actually use hota hai aur interview mein actually poocha jaata hai. Kitaabi topics jo industry mein kahin nahi chalte, wo yahan nahi milenge.
What you’ll learn
- ●Java programs ko concurrency kyun chahiye
- ●Thread banana: Thread vs Runnable
- Thread lifecycle, sleep, join, interruptFree account
- synchronized keyword aur explicit LocksFree account
- volatile aur Java Memory ModelFree account
- wait/notify aur producer-consumer patternFree account
- Race conditions, deadlock, livelock, starvationFree account
- ExecutorService aur thread poolsFree account
- Callable aur FutureFree account
- CompletableFuture: async chainingFree account
- ForkJoinPool aur parallelStream ke peeche ka common poolFree account
- Concurrent collections: ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueueFree account
- Atomic classes aur compare-and-swapFree account
- CountDownLatch, CyclicBarrier, SemaphoreFree account
- RecapFree account
- ●Project: Multi-threaded Word Counter
- Project: Producer-Consumer Task QueueFree account
- Project: Parallel File ProcessorFree account
Java programs ko concurrency kyun chahiye
Ek restaurant mein agar chef ek hi ho to wo ek waqt mein ek hi kaam kar sakta hai - kaatna, phir hilaana, phir plate lagaana. Doosra chef aa jaaye to kaatna aur hilaana dono saath ho sakte hain. Concurrency wahi doosra chef hai: ek program jo ek se zyada kaam overlapping time mein karta hai. Sirf ek CPU core wali machine par ye overlap ek illusion hai jo OS bahut tezi se tasks ke beech switch karke banata hai. Kai cores wali machine par kuch overlap sach mein ek hi waqt mein hota hai - isi khaas case ko parallelism kehte hain.
Process vs thread. Process ek azaad chalta hua program hai apni khud ki memory ke saath - aapka browser aur IDE do process hain, ek crash ho to doosra nahi girta. Thread ek halka execution unit hai jo process ke andar rehta hai; har process mein kam se kam ek thread hota hai (main thread), aur ek hi process ke threads ek hi heap memory share karte hain. Yahi sharing is poore chapter ki wajah hai: shared memory hi do threads ko tez coordinate karna aasan banati hai, aur yahi galat coordinate karne par khatarnaak bhi bana deti hai - Ch7 se aage sab kuch usi shared access ko safely control karne ke baare mein hai.
Multitasking, multithreading, multiprocessing. Multitasking matlab OS kai processes ek saath chalata hai (aapka OS browser aur IDE ko jaggle kar raha hai). Multithreading matlab ek hi process kai threads chalata hai. Multiprocessing matlab machine ke paas ek se zyada CPU hain jo sach mein ek se zyada cheez ek hi pal mein chala rahe hain. Single-core machine multitask aur multithread kar sakti hai par sach mein multiprocess nahi kar sakti - wo itni tezi se switch karke simultaneity ka bhram deti hai ki insaan pakad nahi paata.
Concurrency vs parallelism - jo asal mein poocha jaata hai. Concurrency ek hi time window mein ek se zyada task manage karne ke baare mein hai; parallelism theek ek hi pal mein ek se zyada task execute karne ke baare mein hai. Ek core do threads ko jaggle kare to concurrent hai, parallel nahi. Quad-core machine sach mein char threads ek saath chalaye to dono hai. Har parallel program concurrent hai, par har concurrent program parallel nahi - Node.js ka single-threaded event loop concurrency bina parallelism ka mashhoor misaal hai.
Java mein iski zarurat kyun
- CPU-bound kaam multi-core hardware par tez ho jaata hai. Azaad tukdon mein kaam thodo aur threads ko de do, OS scheduler unhe alag cores par theek ek hi waqt mein chala sakta hai.
- I/O-bound kaam baaki sab ko rokna band kar deta hai. File padhne wala, dheemi API call karne wala, ya database ka intezaar karne wala thread UI ko freeze ya doosri requests ko rokna nahi chahiye - ek request-per-thread wala server ek ke intezaar mein doosron ko serve kar sakta hai.
- Responsiveness. Jo GUI ya web server kabhi kuch bhi concurrently na kare wo kisi bhi dheemi operation par turant frozen mehsoos hoga.
Kimat, saaf-saaf pehle hi bata di jaye, yahi poora baaki chapter hai: memory share karne wale threads use corrupt kar sakte hain, uske liye race kar sakte hain, ya usi par deadlock ho sakte hain. Yahan se aage har topic concurrency ki speed bina uski kimat ke lene ka auzaar hai.
Kab concurrency use karein: I/O-bound kaam jo warna baaki sab kuch rok deta (dheemi API call, file read), ya CPU-bound kaam jise kai cores mein azaad tukdon mein baanta ja sake.
Kab NAHI use karein / trade-off: mutthi bhar chhote, tez, azaad operations ke liye - thread banana aur coordinate karna asli overhead ki kimat rakhta hai, aur chhote workload ke liye ek saadha sequential loop dono - aasan aur tez hai. Asli kimat wahi hai jo baaki poora chapter sambhalna sikhaata hai: shared memory jo laapravaahi se access hone par corrupt, race, ya deadlock ho sakti hai.
Standard definition: Concurrency ek program ki wo kshamta hai jismein wo ek se zyada tasks manage kare jo time mein overlap karte hain; parallelism zyada mazboot, hardware-backed case hai jahan ek se zyada tasks sach mein ek hi pal mein execute hote hain. Process ek azaad memory-isolated chalta hua program hai; thread ek halka, azaad taur pe schedule hone laayak execution path hai jo apne process ke heap ko us process ke har doosre thread ke saath share karta hai.
public class Demo {
static long busyWork(long iterations) {
long sum = 0;
for (long i = 0; i < iterations; i++) sum += i % 7;
return sum;
}
public static void main(String[] args) throws InterruptedException {
long n = 400_000_000L;
long t0 = System.nanoTime();
busyWork(n);
busyWork(n);
long singleThreadMs = (System.nanoTime() - t0) / 1_000_000;
System.out.println("A single-threaded, two halves sequentially took ~" + singleThreadMs + " ms");
long t1 = System.nanoTime();
Thread worker1 = new Thread(() -> busyWork(n));
Thread worker2 = new Thread(() -> busyWork(n));
worker1.start();
worker2.start();
worker1.join();
worker2.join();
long twoThreadMs = (System.nanoTime() - t1) / 1_000_000;
System.out.println("B same work split across 2 threads took ~" + twoThreadMs + " ms");
System.out.println("C available processors on this machine = " + Runtime.getRuntime().availableProcessors());
System.out.println("D this JVM process id = " + ProcessHandle.current().pid());
}
}Thread banana: Thread vs Runnable
Java thread ka kaam batane ke do tareeke deta hai. Thread ko extend kariye aur run() override kariye - ab aapke paas ek naya Thread hai jise ek khaas kaam karna aata hai. Runnable ko implement kariye aur use plain Thread ko de dijiye - aapne kaam ko ek alag rakhe jaane laayak behaviour ki tarah bataya hai, aur koi bhi Thread (ya baad mein koi bhi ExecutorService) use kar sakta hai.
Runnable prefer kariye. Java mein multiple inheritance nahi hoti, isliye Thread extend karna aapki ek extends slot jala deta hai - jo class pehle se kuch aur extend karti hai wo Thread ko extend nahi kar sakti. Runnable mein kuch kharch nahi hota: aapki class jo chahe extend karne ke liye azaad rehti hai, aur wahi Runnable kai threads par reuse ho sakta hai ya seedha thread pool ko de sakte hain, jo asal production code mein thread banane ka ekmatr tareeka hai (executorservice-and-thread-pools).
start() vs run() - is chapter ka sabse zyada poocha jaane wala sawaal
run() seedha call karna naya thread nahi banata - ye ek saada method call hai jo jis bhi thread ne call kiya usi par synchronously chalta hai. start() call karna JVM se ek naya OS-backed thread mangwata hai, aur wahi naya thread aakhir mein run() call karta hai. Neeche ka demo isse saabit karta hai: t3.run() main thread ka naam print karta hai, naya nahi, jabki t1.start() aur t2.start() alag-alag thread naam print karte hain.
start() ek Thread object par sirf ek baar call ho sakta hai. Dobara call karne par IllegalThreadStateException aata hai, kyunki Thread object ka lifecycle sirf aage badhta hai (thread-lifecycle poori state machine batata hai) - koi rasta nahi hai jisse TERMINATED thread wapas NEW ho jaaye. Kaam dobara karna ho to naya Thread object banaiye.
Default thread naming
Bina naam diye banaya Thread Thread-0, Thread-1, Thread-2... paata hai, banane ke order mein, poore process bhar mein - ye ek JVM-managed counter hai, kisi parent thread ke liye alag nahi hota. Threads ko explicit naam dena (new Thread(r, "worker-2")) chhote example se aage kisi bhi cheez ke liye faayde ka sauda hai, kyunki worker-2 wala thread dump ya stack trace Thread-7 wale se turant zyada kaam ka hota hai.
Kab kya use karein: lagbhag har jagah Runnable (ya, aaj ke code mein, use implement karti hui lambda) - ye executors ke saath fit baithta hai aur inheritance kharch nahi karta. Thread sirf tab extend kariye jab aap sach mein ek khaas kism ka thread bana rahe hon jise extra state ya overridden thread-level behaviour chahiye, sirf ek kaam chalaana nahi.
Trade-off: dono tareekon mein, har task ke liye ek raw new Thread(...) scale nahi karta - har ek asli OS resources kharch karta hai aur uski lifecycle koi manage nahi karta. Is topic ke do constructors sirf vocabulary hain; executorservice-and-thread-pools mein wo kimat mutthi bhar se zyada threads ke liye theek se manage hoti hai.
Standard definition: Thread ka kaam ya to Thread ko subclass karke aur run() override karke diya ja sakta hai, ya Runnable functional interface implement karke aur uska instance Thread constructor ko de kar; doosra tareeka idiomatic hai kyunki wo Java ki akeli inheritance slot kharch nahi karta aur executors samet kai execution contexts mein reusable hai. start() naye OS thread ko run() asynchronously call karne ke liye schedule karta hai; run() seedha call karna use calling thread par synchronously chalata hai aur koi naya thread banata hi nahi.
public class Demo {
static class GreetThread extends Thread {
public void run() { System.out.println("A extends Thread: " + Thread.currentThread().getName()); }
}
public static void main(String[] args) throws InterruptedException {
GreetThread t1 = new GreetThread();
t1.start();
t1.join();
Runnable r = () -> System.out.println("B implements Runnable: " + Thread.currentThread().getName());
Thread t2 = new Thread(r, "worker-2");
t2.start();
t2.join();
Thread t3 = new Thread(r, "worker-3");
t3.run();
System.out.println("C after run() directly, thread name still = " + Thread.currentThread().getName());
Thread t4 = new Thread(r);
System.out.println("D default name pattern = " + t4.getName());
}
}Project: Multi-threaded Word Counter
Ye project teen topics ko jod kar ek chalne wala program banata hai: creating-threads (har chunk ke liye ek Runnable job), callable-and-future (ExecutorService.submit(Callable<...>) jo har chunk ke partial result ke liye ek Future deta hai), aur concurrent-collections (partial results ko safely jodne ke liye ConcurrentHashMap).
Shape ye hai: text ki poori body ko azaad chunks mein baanto, har chunk ke liye ek Callable<Map<String,Integer>> fixed thread pool ko submit karo (har Callable apne local HashMap mein words ginta hai, isliye ek akele task ke andar koi shared mutable state nahi - sirf jodte waqt hai), har chunk ka Future jama karo, aur har chunk ka result ConcurrentHashMap mein merge(key, value, Integer::sum) se jodo, jo un keys ka jod atomically karta hai jo do chunks share karte hain.
Har task ke liye local HashMap phir baad mein ConcurrentHashMap mein merge kyun, shuru se hi har task ko ek shared ConcurrentHashMap mein seedha likhne ke bajaye? Dono sahi hain, par per-task local maps counting phase mein koi contention hi paida nahi hote - har thread poori tarah azaad kaam karta hai - aur synchronization ki kimat sirf ek baar chukaate hain, kaafi chhote merge step mein. Shuru se poore counting phase bhar har thread se ek shared map mein seedha likhna bhi kaam karega (ConcurrentHashMap iske liye safe hai) par poore waqt usi map par contend karta hai, sirf akhir mein thodi der ke bajaye.
Java threads aur JVM ke baare mein teen overlapping sentences par chalane par: 'java' 3 baar aata hai, 'jvm' 3 baar, 'threads' 3 baar, aur counts chunk boundaries ke paar sahi tarah jud jaate hain chahe 'java' har akele chunk mein aaye - saabit karta hai ki merge step, sirf per-chunk counting nahi, asli kaam kar raha hai.
Standard definition: Ek multithreaded word counter map-reduce shape dikhaata hai jo asli concurrent data processing mein aam hai: input ko azaad chunks mein baanto, har chunk ko ExecutorService/Callable/Future se concurrently process karo bina processing ke dauraan kisi shared state ke, phir har chunk ke azaad result ko ek final structure mein reduce (merge) karo, thread-safe collection ya sirf merge boundary par explicit synchronization istemal karke.
import java.util.*;
import java.util.concurrent.*;
public class Demo {
static Map<String, Integer> countWords(String chunk) {
Map<String, Integer> local = new HashMap<>();
for (String w : chunk.toLowerCase().split("[^a-z0-9]+")) {
if (w.isBlank()) continue;
local.merge(w, 1, Integer::sum);
}
return local;
}
public static void main(String[] args) throws Exception {
List<String> chunks = List.of(
"Java threads run concurrently on the JVM",
"The JVM schedules Java threads across cores",
"Concurrency in Java uses threads and the JVM scheduler");
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<Map<String, Integer>>> futures = new ArrayList<>();
for (String chunk : chunks) futures.add(pool.submit(() -> countWords(chunk)));
ConcurrentHashMap<String, Integer> total = new ConcurrentHashMap<>();
for (Future<Map<String, Integer>> f : futures) {
for (var entry : f.get().entrySet()) {
total.merge(entry.getKey(), entry.getValue(), Integer::sum);
}
}
pool.shutdown();
System.out.println("A total distinct words = " + total.size());
System.out.println("B count of 'java' = " + total.get("java"));
System.out.println("C count of 'jvm' = " + total.get("jvm"));
System.out.println("D count of 'threads' = " + total.get("threads"));
int grandTotal = total.values().stream().mapToInt(Integer::intValue).sum();
System.out.println("E sum of all word counts = " + grandTotal);
}
}Concurrencyinterview questions & answers
10 sample questions below — 205+ in the full bank inside.
thenApply() kya karta hai, aur chained thenApply calls kaise behave karti hain?
thenApply(fn) upstream stage ka result aate hi use transform karta hai, transformed value ke liye ek naya CompletableFuture return karta hai - chains left se right compose hoti hain, Stream operations jaisi, sivaay iske ki har step sirf pichhle ke asal mein asynchronously khatam hone ke baad hi chalta hai. Verify kiya: supplyAsync(() -> 10).thenApply(n -> n*2).thenApply(n -> n+1) ne 21 diya.
In simple terms: Stream-pipeline se milta-julta hona samajh banaane ke liye kaam ka hai, par asli farak explicitly kehna zaroori hai: Stream pipeline lazy hai aur chalte waqt synchronous hai; CompletableFuture chain har stage par sach mein asynchronous hai.
thenApply ke bajaye thenAccept kab istemal karenge?
thenApply result ko transform karta hai aur wo transformed value liye ek naya CompletableFuture return karta hai, isliye ise tab istemal karein jab baad mein aur chaining karni ho. thenAccept terminal, side-effecting version hai - ye result ko consume karta hai (jaise print karna, save karna) aur CompletableFuture<Void> return karta hai, jab is step ke baad kuch aur compute nahi karna hota.
In simple terms: Ye ek seedha par zaroori API-choice sawaal hai jo check karta hai ki candidate ye dekh kar sahi method chun sakta hai ki koi value aage bhejni hai ya nahi.
Runnable aur Callable mein kya fark hai?
Runnable.run() void return karta hai aur checked exception throw nahi kar sakta. Callable<V>.call() V type ki value return karta hai aur koi bhi checked exception throw karne ke liye declared hai. Callable ko ExecutorService ko submit karne se Future<V> milta hai jo aane wale result ko represent karta hai; Runnable submit karne se Future<?> milta hai jiska result kaamyabi par hamesha null hota hai.
In simple terms: Checked-exception wali kshamta wo hissa hai jise log bhool jaate hain: Callable sirf return values ke liye nahi hai, balki khaas taur pe un tasks ke liye hai jinki asal implementation checked exceptions throw karti hai (file ya network I/O), taaki compile karne ke liye extra wrapping na chahiye.
Kya isDone() ka true return karna iska matlab hai ki task safaltapoorvak poora hua?
Nahi - isDone() true hota hai chahe task normally poora hua ho, exception phenka ho, ya cancel hua ho. Ye sirf itna batata hai ki task KISI terminal state tak pahunch gaya hai, kaunsi state ye nahi; ye theek kaunsi terminal state hai janne ke liye aapko phir bhi get() (result dekhne ya ExecutionException pakadne ke liye) ya isCancelled() chahiye.
In simple terms: Ye isDone() ki ek aam galat samajh ko band karta hai jo ise success indicator maan leti hai, jabki wo explicitly aisa nahi hai.
Agar koi program t.start() ke bajaye t.run() call kare, to run() return hote hote kul kitne threads maujood hote hain?
Ab bhi sirf wahi ek thread jisne call kiya - run() ko seedha call karna ek normal synchronous method call hai, jo caller ke apne thread par chalta hai, koi naya thread kabhi nahi banta. run() ke andar Thread.currentThread().getName() caller ka hi naam batayega (jaise 'main'), kisi naye worker thread ka naam nahi.
In simple terms: Ye chapter ke sabse zyada poochhe jaane wale farak ko thoda alag angle (thread count) se dobara poochta hai, taaki sirf vocabulary nahi, mental model bhi samajh mein aaya ho ye pakka ho sake.
Kai threads ke beech unique IDs banane ke liye AtomicLong natural fit kyun hai?
ID generator ko ek seedha compound operation chahiye (current counter padho, ek unique agli value banao) jo kai threads ke concurrent access ke neeche sahi tarike se ho. AtomicLong.incrementAndGet() theek yehi kaam bina kisi locking overhead ke atomically karta hai - har call ek sach mein unique, kabhi na dohraayi jaane wali value return karta hai chahe kai threads se ek saath call kiya jaaye.
In simple terms: Ye ek practical, asli duniya ka use-case sawaal hai jo atomic classes ko ek pehchani hui application mein grounded karta hai, sirf ek seedha counter demo se aage.
Modern Java mein Runnable banane ke liye lambda expression istemal karna idiomatic tareeka kyun hai?
Runnable ek functional interface hai (ek hi abstract method, run()), isliye lambda expression bina anonymous class likhe seedha use implement kar sakta hai - new Thread(() -> doWork()) padhne mein 'ek thread jiska kaam doWork() hai' jaisa lagta hai, bina kisi extra formality ke, aur functionally poore Runnable implementation jaisa hi hai.
In simple terms: Ye concurrency chapter ki threading vocabulary ko java8-functional ke lambda/functional-interface material se jodta hai, jinhe candidates se jodne ki umeed ki jaati hai.
getAndIncrement() aur incrementAndGet() mein kya fark hai?
Dono value ko atomically badhate hain; sirf return value mein farak hai. getAndIncrement() increment se PEHLE ki value return karta hai - verify kiya: 0 se shuru hokar, ye 0 return karta hai jabki field 1 ban gaya. incrementAndGet() increment ke BAAD ki value return karta hai. Koi bhi zyada sahi nahi hai - caller ko purani ya nayi value chahiye, isi ke hisaab se chuniye.
In simple terms: Ye naming pattern (getAndX purani deta hai, XAndGet nayi deta hai) poori Atomic* API mein consistent hai, getAndSet/updateAndGet samet, isliye ise yahan ek baar seekhna seedha aage kaam aata hai.
Single-value atomics ke alawa, kya java.util.concurrent.atomic poore array ke liye bhi kuch deta hai?
Haan - AtomicIntegerArray, AtomicLongArray, aur AtomicReferenceArray ek array ke individual elements par atomic operations dete hain, taaki ek khaas index ko har element ke liye alag Atomic* wrapper object banaye bina aur poori array lock kiye bina CAS-based atomicity ke saath update kiya ja sake.
In simple terms: Ye chapter ke demo mein focus hui char classes se aage ki breadth check karta hai, ye dekhte hue ki candidate ko pata hai package ka surface sirf single-value wrappers se zyada hai.
Plain Future/Callable CompletableFuture se behtar choice kab hoga?
Ek akeli, seedhi background task ke liye jismein aage koi chaining nahi karni, kisi doosre azaad async result se jodna nahi, ya bina blocking try/catch ke failure handle nahi karna - plain Future/Callable saral hai aur kam API samajhne mein aati hai, aur wahan CompletableFuture ki composition machinery lagana bekaar ki complexity hai.
In simple terms: Ye chapter ki batayi hui 'kab na karein' wali salah hai, ye check karte hue ki candidate CompletableFuture ko har case mein Future se bina-shart behtar maan kar nahi chalta.
195+ more Concurrency questions inside
Create a free account to read the full question bank, learn every topic, and practise with an AI mock interview.
Unlock all questions — freeReady to practise Concurrency?
Unlock every topic free, then face an AI interviewer that asks follow-ups and grades your answers.