Showing posts with label Core Java. Show all posts
Showing posts with label Core Java. Show all posts

Java Thread Methods Explained (start, run, join, wait, notify…)

 

Java Thread Methods Explained (start, run, join, wait, notify…)

📌 What is a Thread in Java?

A thread is a lightweight unit of execution inside a Java program.
Java supports multithreading, allowing multiple tasks to run concurrently.


🧭 Java Thread Lifecycle (Text Diagram)

┌──────────┐ │ NEW │ ← Thread object created └────┬─────┘ │ start() ▼ ┌──────────┐ │ RUNNABLE │ ← Ready / Running └────┬─────┘ │ sleep(), wait(), join() ▼ ┌──────────┐ │ WAITING │ │ /BLOCKED │ └────┬─────┘ │ notify(), timeout over ▼ ┌──────────┐ │ RUNNABLE │ └────┬─────┘ │ run() completes ▼ ┌──────────┐ │ TERMINATED│ └──────────┘

1️⃣ start() — Start a New Thread

🔹 Purpose

  • Creates a new call stack

  • Executes code concurrently

🔹 Example

Thread t = new Thread(() -> { System.out.println("Thread running"); }); t.start();

🔹 Key Point

✔️ start() calls run() internally
❌ Never call start() twice (IllegalThreadStateException)


📌 Interview Tip

start() creates a new thread, run() does not.


2️⃣ run() — Thread Execution Logic

🔹 Purpose

  • Contains the business logic of the thread

🔹 Example

public void run() { System.out.println("Inside run()"); }

🔹 Important

Calling run() directly:

t.run();

❌ Runs like a normal method, NOT a thread


🧠 Diagram

main thread | └── run() ❌ (no new thread)

3️⃣ sleep() — Pause Current Thread

🔹 Purpose

  • Temporarily pauses execution

  • Does not release lock

🔹 Example

Thread.sleep(1000); // 1 second

🔹 Diagram

RUNNING → sleep(1s) → RUNNABLE

🔹 Use Case

  • Retry logic

  • Polling

  • Rate limiting


4️⃣ join() — Wait for Thread to Finish

🔹 Purpose

  • Makes one thread wait for another to complete

🔹 Example

Thread t = new Thread(() -> { System.out.println("Worker thread"); }); t.start(); t.join(); System.out.println("Main thread resumes");

🔹 Diagram

Main Thread | ├── start Worker | └── join() → WAIT | ▼ Worker finishes | ▼ Main resumes

📌 Real-World Use

  • Parallel tasks

  • Batch processing

  • Result aggregation


5️⃣ wait() — Release Lock and Wait

🔹 Purpose

  • Makes thread wait until notified

  • Releases monitor (lock)

🔹 Must Be Called Inside

synchronized(obj) { ... }

🔹 Example

synchronized(lock) { lock.wait(); }

🔹 Diagram

Thread A (holds lock) | └── wait() ↓ releases lock ↓ WAITING

6️⃣ notify() — Wake One Waiting Thread

🔹 Purpose

  • Wakes one thread waiting on the same object

🔹 Example

synchronized(lock) { lock.notify(); }

🔹 Diagram

WAITING Threads | └── notify() → one thread wakes up

7️⃣ notifyAll() — Wake All Waiting Threads

🔹 Purpose

  • Wakes all threads waiting on the object

🔹 Example

synchronized(lock) { lock.notifyAll(); }

🔄 wait() vs sleep()

Featurewait()sleep()
Releases lock✅ Yes❌ No
Needs synchronized✅ Yes❌ No
Wakes by notify✅ Yes❌ No
Used forInter-thread communicationDelay

🔄 notify() vs notifyAll()

notify()notifyAll()
Wakes one threadWakes all threads
Risk of starvationSafer
FasterSlightly slower

📌 Best practice: Prefer notifyAll() in complex systems.


8️⃣ Real-World Producer–Consumer Example

class Shared { boolean dataReady = false; synchronized void produce() throws InterruptedException { while (dataReady) wait(); System.out.println("Producing data"); dataReady = true; notifyAll(); } synchronized void consume() throws InterruptedException { while (!dataReady) wait(); System.out.println("Consuming data"); dataReady = false; notifyAll(); } }

🧠 Flow Diagram

Producer → produce() → notify() Consumer → wait() → consume()

⚠️ Common Mistakes (Interview Favorite)

❌ Calling wait() without synchronized
❌ Calling run() instead of start()
❌ Using notify() instead of notifyAll()
❌ Sleeping while holding locks


🧠 Interview 30-Second Summary

start() creates a new thread, run() contains logic.
sleep() pauses without releasing locks.
join() waits for thread completion.
wait() and notify() enable inter-thread communication and must be used inside synchronized blocks.


🧩 When to Use What?

ScenarioMethod
Start parallel taskstart()
Delay executionsleep()
Wait for resultjoin()
Thread coordinationwait / notify
Multiple waiting threadsnotifyAll

🔑 Final Takeaway

Java threading is powerful but dangerous if misused.
Modern Java often prefers:

  • ExecutorService

  • CompletableFuture

  • ForkJoinPool

…but interviews still expect core thread knowledge.

How JVM Identifies and Communicates Between Stack, Heap, and Method Area

 

How JVM Identifies and Communicates Between Stack, Heap, and Method Area

Java has three types of memory involved during execution:

1️⃣ Method Area

Stores class-level metadata
(Static variables, class names, method definitions, constant pool)

2️⃣ Java Stack

Stores method frames and local variables
(Primitives + references)

3️⃣ Heap

Stores objects, arrays, Strings

The key question:

👉 How does the JVM connect these separate memory areas?

Let’s break it down step-by-step.


1. The Key to Everything: References

Java does not store objects inside the Stack.
The Stack only stores a reference (like a pointer) pointing to the object in the Heap.

Example:

Product p = new Product();

JVM Memory View:

Java Stack: p ---> #A13F22 (reference) Heap: Object Product { ... } at #A13F22

p does not contain the object itself
✔ It contains an identifier (reference address)
✔ JVM uses that reference to locate the object inside the Heap


2. What is a “Reference Address”?

It is not the real OS memory address.
Java hides OS addresses for safety.

Instead, the JVM uses:

  • Object handles

  • Internal pointers

  • Indirection tables

  • Metadata tables

Depending on HotSpot implementation, objects are tracked either by:

Direct Pointer: reference → object in heap

or

Handle Table: referencehandleobject in heap

Either way:

A reference uniquely identifies and locates the object in the Heap.


3. How Stack Talks to Heap (Using References)

Example:

int x = 10; Product p = new Product(); p.name = "iPhone";

Java Stack:

Frame(main): x = 10 (primitive) p = #A13F22 (reference)

Heap:

#A13F22 → Product object { name"iPhone" }

Flow

  1. JVM looks inside Stack

  2. Reads p

  3. Finds reference #A13F22

  4. Goes into Heap to fetch object

  5. Finds field name

  6. Accesses string "iPhone"

This is how the Stack communicates with the Heap.


4. How Heap Communicates with Method Area

When creating an object:

Product p = new Product();

JVM first checks Method Area:

Is class Product loaded?

If NOT:

  • Class Loader loads Product.class

  • Method Area stores:

    • Field names

    • Method names

    • Method bytecode

    • Constant pool

    • Static variables

Only after this:

JVM creates an object → places it in Heap.

So each object in heap points back to its class data in Method Area.

Heap object ----> Class metadata in Method Area

This is how the Heap communicates with Method Area.


5. What Identifiers Does the JVM Use?

There are three major identifiers:


1️⃣ Local Variable Identifier (Stack)

Used for:

  • Primitive values (int x)

  • References to heap (Product p)

Stack variables are identified by:

  • Name (x, p)

  • Slot number inside the frame


2️⃣ Object Reference Identifier (Heap)

Each object gets a unique reference (like an internal pointer):

#A13F22 #EF2233 #9C8821

This internal ID is what connects Stack → Heap.


3️⃣ Class Identifier (Method Area)

For each class:

  • Fully Qualified Name: com.example.Product

  • ClassLoader identity

  • Version information

Objects in Heap store a link to their class in Method Area.


6. End-to-End Communication Diagram

┌─────────────┐ │ Java Stack │ │ p = #A13F22│ └──────┬──────┘ │ (reference ID) ▼ ┌───────────────┐ │ Heap │ │ #A13F22 │ │ Product obj │ └──────┬─────────┘ │ (class pointer) ▼ ┌──────────────────────┐ │ Method Area │ │ Class Product {} │ └──────────────────────┘

This is the full path:

Stack variable → object reference → heap objectclass metadata

7. What About Static Variables?

Static variables belong in Method Area.

Accessing them:

Stack variable callsMethod Areastatic value

Example:

Product.count

Heap is not involved at all.


8. What About Methods?

When you call:

p.calculatePrice()

Flow is:

  1. Stack finds reference p

  2. JVM goes to Heap object

  3. Heap object points to Method Area

  4. JVM finds method bytecode

  5. Execution Engine runs the method


9. What About Thread Communication?

Each thread has:

  • Its own Java Stack

  • Its own PC Register

  • Its own Native Method Stack

But all threads share:

  • Method Area

  • Heap

This is why threads can share data (Heap)
but not each other’s local variables (Stack).


Final Simple Summary

✔ Stack

Stores names (variable identifiers)
Stores references (IDs pointing into heap)

✔ Heap

Stores objects
Each object has an internal ID and a link to its class in Method Area

✔ Method Area

Stores class definitions and static values

✔ Communication

Stack → Heap via reference ID
Heap → Method Area via class pointer

Everything begins with references.


One-Line Explanation

The Stack stores references, the Heap stores objects, and the Method Area stores class definitions. JVM connects all three using internal identifiers and pointers.

How JVM Works (Beginner Guide)

 

How JVM Works (Beginner Guide)

From Writing a Java File → Compile → Run → Memory Allocation → Program End

(With Full JVM Architecture Explained Step-by-Step)

Java does not run directly on your computer.
Instead, it runs inside a powerful engine called the Java Virtual Machine (JVM).

Let’s walk through every step, starting from writing a Java program to executing it inside the JVM, using this architecture:

┌──────────────────────────────┐ │ Java Virtual Machine │ ├──────────────────────────────┤ │ Class Loader Subsystem │ ├──────────────────────────────┤ │ Runtime Data Areas │ │ ├─ Method Area │ │ ├─ Heap │ │ ├─ Java Stack │ │ ├─ PC Registers │ │ └─ Native Method Stack │ ├──────────────────────────────┤ │ Execution Engine │ │ ├─ Interpreter │ │ ├─ JIT Compiler │ │ └─ Garbage Collector │ ├──────────────────────────────┤ │ Native Interface (JNI) │ └──────────────────────────────┘

Let’s go step by step.


1. You Create a Java File (Hello.java)

Example:

public class Hello { public static void main(String[] args) { int x = 10; int y = 20; int sum = x + y; System.out.println("Sum: " + sum); } }

What exists now?

  • A plain text file: Hello.java

  • Stored on disk, not in JVM

  • No memory allocation yet

  • JVM has not started yet


2. You Compile the Program (javac Hello.java)

What happens?

✔ Step 1: Java Compiler reads .java file (outside JVM)

  • Checks syntax

  • Converts code to bytecode

  • Produces a new file: Hello.class

✔ Step 2: Hello.class contains bytecode

This is platform-independent, readable by any JVM.

📍 JVM still not running

Memory is still not allocated.


3. You Run the Program (java Hello)

Now JVM starts.

This is where your architecture comes alive.


Stage 1 — Class Loader Subsystem Loads Your Program

When you run:

java Hello

Class Loader Subsystem loads:

  1. Hello.class

  2. Object.class

  3. String.class

  4. System classes from JDK

✔ Where does class metadata go?

➡️ Method Area

Method Area: ├─ Class name (Hello) ├─ Methods (main, constructors) ├─ Fields (none static here) ├─ Constant pool

Stage 2 — Runtime Data Areas Are Created

JVM now prepares memory to run your program.

Runtime Data Areas ├─ Method Areaclass definitions ├─ Heapobjects, arrays ├─ Java Stacklocal variables ├─ PC Registerscurrent instruction of each thread └─ Native Method StackC/C++ calls

Stage 3 — JVM Starts main() Method

JVM creates a new thread called:

main thread

4. JVM Creates a Java Stack for the Main Thread

Each thread gets its own Java Stack.

The stack stores:

  • Method calls

  • Local variables

  • Intermediate calculations

For our program:

Stack Frame for main(): x = ? y = ? sum = ?

5. JVM Executes Bytecode Using the Execution Engine

Execution Engine is the brain:

✔ Interpreter

Executes bytecode line by line.

✔ JIT Compiler

Optimizes frequently run code → machine code
Makes the program faster.

✔ PC Register (Program Counter)

Keeps track of:

Which instruction is running now?

Each thread has a PC Register.


6. JVM Executes Each Line (Memory Flow)

Let’s walk through your code line-by-line and see what happens in each memory area.


🧮 Line 1: int x = 10;

✔ Where is x stored?

➡️ Java Stack (inside main thread stack frame)

Java Stack: x = 10

🧮 Line 2: int y = 20;

Java Stack: x = 10 y = 20

🧮 Line 3: int sum = x + y;

Execution Engine:

  • Reads x from stack

  • Reads y from stack

  • Performs addition using CPU

  • Stores result in stack

Java Stack: x = 10 y = 20 sum = 30

✔ No Heap usage yet
✔ No Garbage Collector activity yet


📝 Line 4: Printing Value

System.out.println("Sum: " + sum);

What happens internally?

  1. JVM creates String "Sum: " → stored in String Pool (Heap — Method Area-backed)

  2. JVM creates a new concatenated String → "Sum: 30" → Heap

  3. println() is a native method → uses Native Method Stack via JNI

Heap: "Sum: " "30" "Sum: 30"

7. Garbage Collector Works (If Needed)

GC checks unused objects in Heap:

  • Temporary strings

  • Temporary objects created in expression evaluation

If no references → GC removes them.

Stack cleanup happens automatically when method returns.


8. End of main() Method

When main() finishes:

  1. Stack frame for main is removed

  2. Local variables (x, y, sum) are gone

  3. Heap objects referenced by nothing → eligible for GC

  4. PC Register for main thread resets

  5. Main thread dies

  6. JVM shuts down (if no other threads exist)


Full Summary with Memory Flow Diagram

Step 1: Write Hello.javaOn Disk Step 2: Compile (javac)Hello.class (bytecode) on Disk Step 3: Run (java Hello)JVM Starts ┌──────────────────────────────────────────────────────────┐ │ JVM Execution Flow │ ├──────────────────────────────────────────────────────────┤ │ Class Loader Subsystem │ │ → Loads Hello.class │ │ → Loads java.lang classes │ ├──────────────────────────────────────────────────────────┤ │ Runtime Data Areas │ │ Method AreaClass definitions │ │ HeapObjects, Strings │ │ Java StackLocal variables, method frames │ │ PC RegisterCurrent instruction │ │ Native StackNative calls │ ├──────────────────────────────────────────────────────────┤ │ Execution Engine │ │ Interpreterexecutes bytecode │ │ JIToptimizes hot code │ │ GCcleans unused heap objects │ └──────────────────────────────────────────────────────────┘ At runtime: Java Stack: x = 10 y = 20 sum = 30 Heap: "Sum: 30" End: - Stack destroyed - Heap cleaned by GC - JVM shutdown

One-Line Takeaway

Stack = method + variables, Heap = objects, Method Area = class info, Execution Engine = runs bytecode, GC = cleans heap, PC Register = tracks instruction.

How Many Ways Can We Create Threads in Java? (2025 Guide)

How Many Ways Can We Create Threads in Java? (2025 Guide)

Java provides 4 main ways to create threads + additional modern concurrency abstractions (Executors, CompletableFuture, ForkJoinPool).


1. Extending the Thread Class

This is the simplest way, but not recommended for large applications because Java does not support multiple inheritance.

✅ Example

class MyThread extends Thread { @Override public void run() { System.out.println("Thread running using Thread class"); } } public class Main { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }

✅ When to use

✔ Quick testing
✔ Very small programs

❌ Not recommended because

  • You lose the ability to extend another class

  • Less flexible


2. Implementing Runnable Interface (Most Common)

This is widely used because we can extend another class and still implement Runnable.

✅ Example

class MyTask implements Runnable { @Override public void run() { System.out.println("Thread running using Runnable"); } } public class Main { public static void main(String[] args) { Thread t = new Thread(new MyTask()); t.start(); } }

✅ When to use

✔ Clean separation of task vs. thread
✔ Recommended for backend apps
✔ Compatible with Executors


3. Implementing Callable Interface (with Future)

Callable is like Runnable, but it returns a value and can throw exceptions.

✅ Example

import java.util.concurrent.*; class MyCallable implements Callable<String> { @Override public String call() { return "Result from Callable"; } } public class Main { public static void main(String[] args) throws Exception { ExecutorService service = Executors.newSingleThreadExecutor(); Future<String> result = service.submit(new MyCallable()); System.out.println(result.get()); service.shutdown(); } }

✅ When to use

✔ When you need a return value
✔ When exception handling matters


4. Using ExecutorService (Modern, Scalable)

This is how real backend systems create threads.

Instead of creating threads manually, we submit tasks to a thread pool.

✅ Example

ExecutorService executor = Executors.newFixedThreadPool(3); executor.submit(() -> { System.out.println("Running in a thread pool"); }); executor.shutdown();

✅ When to use

✔ Production-grade apps
✔ Web servers / microservices
✔ High performance & resource management


5. Using CompletableFuture (Asynchronous + Functional)

The most modern approach introduced in Java 8.
Supports async pipelines, chaining, non-blocking tasks.

✅ Example

CompletableFuture.runAsync(() -> { System.out.println("Async task running"); });

Or returning values:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello"); System.out.println(future.join());

✅ When to use

✔ Async programming
✔ Non-blocking services
✔ Combining results from multiple threads
✔ Microservices / reactive pipelines


6. Using ForkJoinPool (Parallel computation)

Useful for recursive tasks and parallel algorithms.

✅ Example

ForkJoinPool.commonPool().submit(() -> System.out.println("ForkJoin thread running") ).join();

✅ When to use

✔ Parallel processing
✔ Complex computations (divide & conquer)


Summary Table (Best for Blog)

MethodDescriptionWhen to UseSupports Return Value
Thread classExtend Thread and override run()Simple, basic examples❌ No
RunnableImplement task logicMost common❌ No
CallableLike Runnable but returns valueNeed result or exceptions✅ Yes
ExecutorServiceThread poolProduction-level threading✅ Yes
CompletableFutureAsync pipelineNon-blocking apps✅ Yes
ForkJoinPoolParallel computationHeavy CPU tasks✅ Yes

Diagram – Thread Creation in Java (Text-Based)

+--------------------+ | Create Thread | +--------------------+ | ----------------------------------------------------- | | | | Thread Runnable Callable Executors | | | | start() new Thread() via Executor submit(Runnable/ submit() Callable) | +----------+ | Thread | | Pool | +----------+

Confusion Matrix + Precision/Recall (Super Simple, With Examples)

  Confusion Matrix + Precision/Recall (Super Simple, With Examples) 1) Binary Classification Setup Binary classification means the model p...

Featured Posts