Threads
Threads
A thread is the smallest unit of CPU execution within a process. Threads allow a program to perform multiple tasks concurrently.
What is a Thread?
- Thread: Lightweight process, basic unit of CPU utilization
- Also called Lightweight Process (LWP)
- Multiple threads can exist within a single process
- Threads share process resources but execute independently
Thread Components
Private to Each Thread (Not Shared)
| Component | Description |
|---|---|
| Thread ID | Unique identifier for the thread |
| Program Counter (PC) | Tracks current instruction |
| Register Set | CPU registers state |
| Stack | Local variables, function calls |
Shared Among Threads (Same Process)
| Component | Description |
|---|---|
| Code Section | Program instructions |
| Data Section | Global variables |
| Heap | Dynamically allocated memory |
| Open Files | File descriptors |
| Signals | Signal handlers |
Thread vs Process
| Aspect | Process | Thread |
|---|---|---|
| Definition | Program in execution | Unit of execution within process |
| Memory | Separate memory space | Shared memory space |
| Creation Time | Slower (heavy) | Faster (lightweight) |
| Context Switch | Expensive | Cheaper |
| Communication | IPC required | Direct (shared memory) |
| Isolation | Independent | Dependent on process |
| Resource Overhead | High | Low |
| Crash Impact | Isolated | Can crash entire process |
Types of Threads
Based on Number
Single-Threaded Process
- Only one thread of execution
- Simple to implement
- No concurrency within process
- Example: Traditional UNIX programs
Multi-Threaded Process
- Multiple threads within same process
- Better resource utilization
- Complex synchronization needed
- Example: Web browsers, servers
Based on Level
User-Level Threads (ULT)
- Managed by user-level thread library
- Kernel unaware of threads
- Fast creation and switching
- Entire process blocks on I/O
Advantages:
- Thread switching doesn't require kernel mode
- Can run on any OS
- Scheduling is application-specific
- Fast and efficient
Disadvantages:
- One thread blocks → entire process blocks
- Cannot utilize multiple CPUs
- No true parallelism
Kernel-Level Threads (KLT)
- Managed directly by OS kernel
- Kernel aware of each thread
- Can schedule threads on multiple CPUs
- System call for each operation
Advantages:
- True parallelism on multi-core
- One thread blocks, others continue
- Better for I/O-bound applications
Disadvantages:
- Slower creation and management
- Context switch requires kernel intervention
- More overhead
Multithreading Models
Many-to-One Model
User Threads: T1 T2 T3 T4
\ | | /
\ | | /
Kernel Thread: KT1- Many user threads → One kernel thread
- Thread management in user space
- Entire process blocks if one thread blocks
- No parallel execution
- Example: Green threads (early Java)
One-to-One Model
User Threads: T1 T2 T3 T4
| | | |
Kernel Threads: KT1 KT2 KT3 KT4- Each user thread → One kernel thread
- More concurrency than many-to-one
- Creating user thread creates kernel thread
- Overhead of creating kernel threads
- Example: Windows, Linux
Many-to-Many Model
User Threads: T1 T2 T3 T4 T5
\ | X | /
\ | / \ | /
Kernel Threads: KT1 KT2 KT3- Many user threads → Many kernel threads
- Number of kernel threads ≤ user threads
- Best of both worlds
- OS creates sufficient kernel threads
- Example: Windows with ThreadFiber
Two-Level Model
- Variation of many-to-many
- Allows binding user thread to kernel thread
- Provides flexibility
- Example: IRIX, HP-UX
Thread Libraries
POSIX Threads (Pthreads)
#include <pthread.h>
void *thread_function(void *arg) {
printf("Hello from thread!\n");
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
pthread_join(thread, NULL);
return 0;
}Key Functions:
| Function | Description |
|---|---|
pthread_create() |
Create new thread |
pthread_join() |
Wait for thread termination |
pthread_exit() |
Terminate calling thread |
pthread_cancel() |
Request thread cancellation |
pthread_self() |
Get thread ID |
Java Threads
// Method 1: Extend Thread class
class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
// Method 2: Implement Runnable
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable running");
}
}
// Usage
new MyThread().start();
new Thread(new MyRunnable()).start();Windows Threads
#include <windows.h>
DWORD WINAPI ThreadFunc(LPVOID param) {
printf("Windows Thread\n");
return 0;
}
int main() {
HANDLE thread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return 0;
}Thread States
┌─────────────────┐
│ NEW │
└────────┬────────┘
│ start()
▼
┌────────────────────────────────────────┐
│ RUNNABLE │
│ ┌──────────┐ ┌──────────┐ │
│ │ READY │◄────►│ RUNNING │ │
│ └──────────┘ └──────────┘ │
└────────┬───────────────┬───────────────┘
│ │
I/O, sleep, │ │ run() completes
wait, lock │ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ BLOCKED/ │ │ TERMINATED │
│ WAITING │ │ │
└──────────────┘ └──────────────┘| State | Description |
|---|---|
| New | Thread created but not started |
| Runnable | Ready to run or running |
| Blocked | Waiting for monitor lock |
| Waiting | Waiting indefinitely for another thread |
| Timed Waiting | Waiting for specified time |
| Terminated | Execution completed |
Benefits of Threads
1. Responsiveness
- Application remains responsive during long operations
- UI thread separate from worker threads
- User can continue interacting
2. Resource Sharing
- Threads share process memory and resources
- No need for IPC mechanisms
- Efficient communication
3. Economy
- Cheaper to create than processes
- Less memory overhead
- Faster context switching
4. Scalability
- Utilize multiple CPU cores
- Parallel execution possible
- Better performance on multi-core systems
Thread Challenges
1. Race Conditions
- Multiple threads access shared data
- Result depends on execution order
- Need synchronization mechanisms
2. Deadlocks
- Threads waiting for each other
- Circular dependency
- System hangs
3. Priority Inversion
- Low-priority thread holds resource
- High-priority thread waits
- Solution: Priority inheritance
4. Thread Safety
- Code must be thread-safe
- Use synchronization primitives
- Avoid shared mutable state
Thread Synchronization Primitives
| Primitive | Purpose |
|---|---|
| Mutex | Mutual exclusion lock |
| Semaphore | Counting synchronization |
| Condition Variable | Wait for condition |
| Read-Write Lock | Multiple readers, single writer |
| Barrier | Synchronization point |
| Spinlock | Busy-wait lock |
Important Interview Questions
Q1: Difference between process and thread?
A: Process is an independent program with separate memory space. Thread is a lightweight execution unit within a process that shares memory with other threads of the same process.
Q2: Why use threads over processes?
A: Threads are lighter, share memory (easy communication), faster to create/switch, and more efficient for concurrent tasks within same application.
Q3: What is thread safety?
A: Code is thread-safe when it functions correctly during simultaneous execution by multiple threads, typically achieved through synchronization.
Q4: User-level vs Kernel-level threads?
A: ULT - managed by library, fast but no true parallelism. KLT - managed by OS, slower but enables real parallelism on multiple CPUs.
Q5: What happens when a thread crashes?
A: The entire process crashes since threads share the same address space. One thread's memory corruption affects all threads.
Quick Reference
Thread Creation Cost: Thread < Process
Context Switch Cost: Thread < Process
Communication: Thread (shared memory) faster than Process (IPC)
Isolation: Process > Thread
Parallelism: KLT > ULT