0% found this document useful (0 votes)
2 views18 pages

Java Multi Threading

Uploaded by

jonas48490
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views18 pages

Java Multi Threading

Uploaded by

jonas48490
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Java Multithreading:

Complete Interview Guide


Table of Contents
1. Fundamentals
2. Creating Threads
3. Thread Lifecycle
4. Synchronization
5. Locks and Concurrent Utilities
6. Thread Communication
7. Common Patterns
8. Interview Questions
9. Best Practices

Fundamentals
What is Multithreading?
 Thread: A lightweight process; smallest unit of execution in a program
 Multithreading: Running multiple threads concurrently within a single process
 Allows responsive, efficient applications (e.g., UI remains responsive while background work
happens)

Process vs Thread

Aspect Process Thread


Separate memory Shared memory
Memory
space space
Creation
Heavy Light
Cost
Context
Expensive Less expensive
Switch
Communicati Easy (shared
Complex (IPC)
on memory)

Key Concepts
 Concurrency: Multiple tasks making progress (may not run simultaneously)
 Parallelism: Multiple tasks running simultaneously on multiple cores
 Race Condition: When thread safety is violated; output depends on timing
 Deadlock: Threads waiting for each other indefinitely
 Starvation: Thread never gets CPU time
Creating Threads
Method 1: Extend Thread Class
class MyThread extends Thread {
@Override
public void run() {
[Link]("Thread is running");
}
}

// Usage
MyThread thread = new MyThread();
[Link](); // Never call run() directly!

Method 2: Implement Runnable (Preferred)


class MyRunnable implements Runnable {
@Override
public void run() {
[Link]("Thread is running");
}
}

// Usage
Thread thread = new Thread(new MyRunnable());
[Link]();

// Or with lambda (Java 8+)


Thread thread = new Thread(() -> [Link]("Thread is
running"));
[Link]();

Why Runnable is Better


 Java doesn't support multiple inheritance; you might need to extend another class
 Implements an interface describes what the task does, not what it is
 More flexible design

Important: start() vs run()


[Link](); // ✓ Creates new thread, calls run()
[Link](); // ✗ Calls run() in current thread (no parallelism!)

Thread Lifecycle
States
NEW → RUNNABLE ↔ RUNNING → TERMINATED

WAITING/BLOCKED
Thread States (Java)

1. NEW: Thread created but not started


2. RUNNABLE: Thread is ready or running
3. BLOCKED: Waiting to acquire a monitor lock
4. WAITING: Waiting for another thread (wait(), join())
5. TIMED_WAITING: Waiting with a timeout
6. TERMINATED: Execution complete

Getting Thread Information


Thread thread = [Link]();
[Link]([Link]()); // Thread name
[Link]([Link]()); // Thread ID
[Link]([Link]()); // Priority (1-10)
[Link]([Link]()); // Current state
[Link]([Link]()); // Still running?
[Link]([Link]()); // Is it a daemon?

Thread Methods
// Join - Wait for thread to complete
Thread t = new Thread(() -> {});
[Link]();
[Link](); // Main thread waits for t to finish
[Link](1000); // Wait max 1000ms

// Sleep - Pause thread


[Link](1000); // Sleep for 1 second

// Yield - Hint to scheduler to run other threads


[Link]();

// Interrupt - Request thread to stop


[Link]();
if ([Link]().isInterrupted()) {
// Respond to interrupt
}

// Priority (1 = MIN, 5 = NORM, 10 = MAX)


[Link](Thread.MAX_PRIORITY);

// Daemon threads
[Link](true); // Set before start()
// JVM exits when only daemon threads remain

Synchronization
Problem: Race Condition
class Counter {
private int count = 0;

public void increment() {


count++; // NOT atomic! Three steps: read, increment, write
}
}

// Two threads increment simultaneously → lost update

Solution 1: Synchronized Method


class Counter {
private int count = 0;

public synchronized void increment() {


count++;
}

public synchronized int getCount() {


return count;
}
}

How it works:

 Only one thread can execute synchronized method at a time


 Uses implicit lock (monitor) on the object
 Acquired on entry, released on exit or exception

Solution 2: Synchronized Block


class Counter {
private int count = 0;
private Object lock = new Object();

public void increment() {


synchronized(lock) {
count++;
}
}

public int getCount() {


synchronized(lock) {
return count;
}
}
}

Advantages:

 More granular control


 Can synchronize only critical section
 Better performance (less code locked)
 Can use different locks

Solution 3: Static Synchronized


class MyClass {
private static int count = 0;
public static synchronized void increment() {
count++;
}
}

Note: Locks on the Class object, not instances

Volatile Keyword
class Flag {
private volatile boolean running = true;

public void stop() {


running = false;
}

public void process() {


while(running) {
// Guarantees fresh read of 'running' each iteration
}
}
}

Volatile provides:

 Visibility: Changes visible to all threads


 Atomicity: Only for simple reads/writes
 NOT sufficient for compound operations (read-modify-write)

Locks and Concurrent Utilities


ReentrantLock (Explicit Lock)
import [Link];

class Counter {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();

public void increment() {


[Link]();
try {
count++;
} finally {
[Link]();
}
}

public int getCount() {


[Link]();
try {
return count;
} finally {
[Link]();
}
}
}

Advantages over synchronized:

 Explicit lock/unlock (more control)


 tryLock() - non-blocking attempt
 Interruptible locks
 Fair queuing option
 Multiple conditions per lock

ReentrantLock with tryLock()


if ([Link]()) {
try {
// Critical section
} finally {
[Link]();
}
} else {
// Lock not available
}

// With timeout
if ([Link](1, [Link])) {
try {
// Critical section
} finally {
[Link]();
}
} else {
// Timeout
}

ReadWriteLock
import [Link];
import [Link];

class Cache {
private Map<String, String> data = new HashMap<>();
private ReadWriteLock lock = new ReentrantReadWriteLock();

public String get(String key) {


[Link]().lock();
try {
return [Link](key);
} finally {
[Link]().unlock();
}
}

public void put(String key, String value) {


[Link]().lock();
try {
[Link](key, value);
} finally {
[Link]().unlock();
}
}
}

Benefits:

 Multiple readers simultaneously


 Writers get exclusive access
 Better for read-heavy applications

AtomicInteger, AtomicLong, AtomicReference


import [Link];

class Counter {
private AtomicInteger count = new AtomicInteger(0);

public void increment() {


[Link](); // Atomic operation
}

public int getCount() {


return [Link]();
}

// Other atomic operations


public void compareAndSet() {
[Link](5, 10); // If 5, set to 10
}
}

Use when:

 Simple atomic operations needed


 No compound operations
 High concurrency scenarios

CountDownLatch
import [Link];

// Main thread waits for multiple tasks


CountDownLatch latch = new CountDownLatch(3);

Thread t1 = new Thread(() -> {


[Link]("Task 1 done");
[Link]();
});

Thread t2 = new Thread(() -> {


[Link]("Task 2 done");
[Link]();
});

Thread t3 = new Thread(() -> {


[Link]("Task 3 done");
[Link]();
});

[Link](); [Link](); [Link]();

[Link](); // Main waits for all countDowns


[Link]("All tasks complete");

CyclicBarrier
import [Link];

CyclicBarrier barrier = new CyclicBarrier(3, () ->


[Link]("All threads reached barrier!")
);

for (int i = 0; i < 3; i++) {


new Thread(() -> {
[Link]([Link]().getName() + "
waiting");
[Link](); // Wait for others
[Link]([Link]().getName() + "
continuing");
}).start();
}

Semaphore
import [Link];

Semaphore semaphore = new Semaphore(2); // Max 2 concurrent threads

new Thread(() -> {


try {
[Link]();
[Link]("Resource acquired");
[Link](2000);
} catch (InterruptedException e) {
[Link]().interrupt();
} finally {
[Link]();
}
}).start();

Phaser
import [Link];

Phaser phaser = new Phaser(3); // 3 parties

for (int i = 0; i < 3; i++) {


new Thread(() -> {
[Link]("Phase 1: " +
[Link]().getName());
[Link]();

[Link]("Phase 2: " +
[Link]().getName());
[Link]();
}).start();
}

Thread Communication
wait(), notify(), notifyAll()
class ProducerConsumer {
private List<Integer> buffer = new ArrayList<>();
private int capacity = 10;

public synchronized void produce() throws InterruptedException {


while ([Link]() == capacity) {
wait(); // Wait until consumer removes item
}
[Link](1);
notifyAll(); // Wake up waiting consumers
}

public synchronized Integer consume() throws InterruptedException


{
while ([Link]()) {
wait(); // Wait until producer adds item
}
Integer item = [Link](0);
notifyAll(); // Wake up waiting producers
return item;
}
}

Important Rules:

 Must be in synchronized block/method


 Called on object lock
 wait() releases lock, reacquires after notification
 Use while loop (spurious wakeups possible)

Using Condition Variables


import [Link];
import [Link];

class Buffer {
private List<Integer> list = new ArrayList<>();
private ReentrantLock lock = new ReentrantLock();
private Condition notEmpty = [Link]();
private Condition notFull = [Link]();
private int capacity = 10;

public void put(Integer value) throws InterruptedException {


[Link]();
try {
while ([Link]() == capacity) {
[Link]();
}
[Link](value);
[Link]();
} finally {
[Link]();
}
}

public Integer take() throws InterruptedException {


[Link]();
try {
while ([Link]()) {
[Link]();
}
Integer value = [Link](0);
[Link]();
return value;
} finally {
[Link]();
}
}
}

Common Patterns
Thread Pool / ExecutorService
import [Link].*;

// Fixed thread pool


ExecutorService executor = [Link](5);

// Submit tasks
for (int i = 0; i < 10; i++) {
[Link](() -> {
[Link]("Task by: " +
[Link]().getName());
});
}

// Shutdown
[Link](); // Don't accept new tasks
[Link](10, [Link]);

// Force shutdown
[Link]();

Future - Get Result from Thread


ExecutorService executor = [Link](2);

Future<Integer> future = [Link](() -> {


[Link](2000);
return 42;
});

try {
Integer result = [Link](); // Blocks until done
[Link]("Result: " + result);
// With timeout
result = [Link](3, [Link]);
} catch (TimeoutException e) {
[Link]("Timeout");
} catch (ExecutionException e) {
[Link]("Task threw exception");
} finally {
[Link]();
}

ConcurrentHashMap
import [Link];

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();


[Link]("key1", 100);
[Link]("key2", 200);

// Safe concurrent operations


[Link]("key3", 300);

// Atomic operations
[Link]("key1", (k, v) -> v + 10);

Callable and CompletableFuture


import [Link];
import [Link];

// CompletableFuture - More powerful than Future


CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);

try {
[Link]([Link]()); // Output: HELLO WORLD
} catch (Exception e) {
[Link]();
}

// Chaining multiple async operations


CompletableFuture
.supplyAsync(() -> 10)
.thenApplyAsync(x -> x * 2)
.thenApplyAsync(x -> x + 5)
.whenComplete((result, exception) -> {
if (exception != null) {
[Link]("Error: " + exception);
} else {
[Link]("Result: " + result);
}
});

Interview Questions
Q1: What's the difference between synchronized and
volatile?

Answer:

 synchronized: Provides mutual exclusion (only one thread at a time) and visibility
 volatile: Provides visibility only; multiple threads can read/write; NOT suitable for compound
operations
 Use volatile for flags, synchronized/locks for complex operations

Q2: What happens if an exception occurs in a


synchronized method?

Answer: The lock is automatically released when the method exits (normal or
exception). The finally block is not needed to release the lock, but always use try-
finally with locks for guaranteed release.

Q3: Can we synchronize constructors?

Answer: No. Constructors cannot be synchronized because each thread gets its own
object instance during construction. There's no shared access to synchronize.

Q4: What is a deadlock? How to prevent it?

Answer: Deadlock occurs when threads wait for each other indefinitely.

Prevention:

 Acquire locks in same order across all threads


 Use timeout with tryLock()
 Avoid circular wait conditions
 Use higher-level constructs (ForkJoinPool, CompletableFuture)

Example of Deadlock:

Thread 1: Lock A → waiting for Lock B


Thread 2: Lock B → waiting for Lock A

Q5: What is the difference between notify() and


notifyAll()?

Answer:

 notify(): Wakes one random waiting thread


 notifyAll(): Wakes all waiting threads
 notifyAll() is safer; use unless you're sure only one thread should wake

Q6: Can we call [Link]() twice on the same


thread?
Answer: No. Throws IllegalThreadStateException. Thread object can only be started
once. Create a new Thread object for each new task.

Q7: What is a daemon thread?

Answer:

 Low-priority background thread


 JVM terminates when only daemon threads remain
 Use: garbage collection, timers, etc.
 Set with setDaemon(true) before start()

Thread daemon = new Thread(() -> [Link]("Daemon"));


[Link](true);
[Link]();

Q8: Difference between ReentrantLock and


synchronized?

Answer:

synchron Reentrant
Feature
ized Lock
Fairness No Optional
tryLock() No Yes
Timeout No Yes
Interruptible No Yes
Multiple
No Yes
Conditions
Code Control Implicit Explicit

Q9: What is the ThreadLocal class?

Answer: Provides thread-isolated storage. Each thread gets its own instance.

ThreadLocal<String> threadLocal = new ThreadLocal<>();

new Thread(() -> {


[Link]("Thread 1");
[Link]([Link]()); // Output: Thread 1
}).start();

new Thread(() -> {


[Link]("Thread 2");
[Link]([Link]()); // Output: Thread 2
}).start();

Use cases: Database connections, SecurityContext in Spring

Q10: What is Fork/Join Framework?


Answer: For divide-and-conquer parallel tasks.

import [Link];
import [Link];

class SumTask extends RecursiveTask<Long> {


private long[] array;
private int start, end;

protected Long compute() {


if (end - start <= 1000) {
long sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
}

int mid = (start + end) / 2;


SumTask left = new SumTask(array, start, mid);
SumTask right = new SumTask(array, mid, end);

[Link]();
long rightResult = [Link]();
long leftResult = [Link]();

return leftResult + rightResult;


}
}

// Usage
ForkJoinPool pool = [Link]();
long result = [Link](new SumTask(largeArray, 0,
[Link]));

Best Practices
1. Prefer Higher-Level Constructs
// ✗ Low level
synchronized(lock) {
// complex logic
}

// ✓ Use concurrent collections/utilities


ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
ExecutorService executor = [Link](10);

2. Always Use try-finally with Locks


// ✗ Bad
[Link]();
// critical section
[Link]();

// ✓ Good
[Link]();
try {
// critical section
} finally {
[Link]();
}

3. Minimize Synchronized Sections


// ✗ Locking too much
public synchronized void process() {
readFile(); // IO operation - very slow!
updateData();
}

// ✓ Lock only what needs it


public void process() {
data = readFile();
synchronized(this) {
updateData();
}
}

4. Immutability Over Synchronization


// ✗ Requires synchronization
class Point {
private int x, y;
public synchronized void move(int dx, int dy) { }
}

// ✓ Immutable - thread-safe by default


final class Point {
private final int x, y;
public Point move(int dx, int dy) {
return new Point(x + dx, y + dy);
}
}

5. Use Appropriate Collections


// Single-threaded: ArrayList, HashMap
List<String> list = new ArrayList<>();

// Multi-threaded alternatives:
List<String> list = [Link](new ArrayList<>());
Map<String, String> map = new ConcurrentHashMap<>();
Set<String> set = [Link](new
ConcurrentHashMap<>());

6. Proper Thread Cleanup


try {
// Execute tasks
} finally {
[Link]();
if (![Link](10, [Link])) {
[Link]();
}
}

7. Handle InterruptedException Properly


// ✗ Wrong - swallows interruption
try {
[Link](1000);
} catch (InterruptedException e) {
[Link](); // Wrong!
}

// ✓ Correct - restores interrupt status


try {
[Link](1000);
} catch (InterruptedException e) {
[Link]().interrupt(); // Restore flag
}

// Or propagate
public void myMethod() throws InterruptedException {
[Link](1000);
}

8. Avoid Common Mistakes


// ✗ Race condition with volatile flag
volatile boolean running = true;
public void run() {
while(running) {
doWork();
}
}

// Problem: Worker thread might not see the update immediately


// Better: Use Atomic or explicit synchronization for compound checks

// ✗ Synchronized on wrong object


private Object lock = new Object();
synchronized(this) { // Wrong lock!
// critical section
}

// ✓ Correct
synchronized(lock) {
// critical section
}

Quick Reference Checklist


 [ ] Understand Thread creation (extend Thread vs Runnable)
 [ ] Know thread states and lifecycle methods
 [ ] Master synchronized keyword usage
 [ ] Understand volatile and its limitations
 [ ] Know ReentrantLock and when to use it
 [ ] Understand wait()/notify() pattern
 [ ] Familiar with concurrent collections
 [ ] Know ExecutorService and thread pools
 [ ] Understand Future and CompletableFuture
 [ ] Know common concurrent utilities (Semaphore, CountDownLatch, CyclicBarrier, Phaser)
 [ ] Avoid deadlocks and race conditions
 [ ] Handle InterruptedException properly
 [ ] Use immutability when possible
 [ ] Familiar with ThreadLocal
 [ ] Know Fork/Join framework basics

Practice Problem: Thread-Safe Counter


public class ThreadSafeCounter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();

public void increment() {


[Link]();
try {
count++;
} finally {
[Link]();
}
}

public int getCount() {


[Link]();
try {
return count;
} finally {
[Link]();
}
}

// Test it
public static void main(String[] args) throws
InterruptedException {
ThreadSafeCounter counter = new ThreadSafeCounter();

Thread[] threads = new Thread[10];


for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
[Link]();
}
});
}

for (Thread t : threads) [Link]();


for (Thread t : threads) [Link]();

[Link]("Final count: " + [Link]()); //


10000
}
}
Additional Resources to Study
1. Java Documentation: [Link] package
2. Books: "Java Concurrency in Practice" (Brian Goetz)
3. Topics to Deep Dive:

o Memory model and visibility


o Happens-before relationships
o JVM optimizations (reordering)
o Performance tuning for concurrent apps

Good luck with your interviews! 🚀

You might also like