Multithreading and Concurrency in Java
Java is a popular programming language for building high-performance applications. One of its key strengths is multithreading, which allows multiple tasks to run concurrently, improving efficiency and responsiveness.
This guide covers the fundamentals, key concepts, and practical usage of multithreading and concurrency in Java.
1. What Is Multithreading?
Multithreading is the ability of a program to execute two or more threads simultaneously. A thread is the smallest unit of execution in a program.
Benefits of Multithreading
Faster execution: Multiple tasks can run at the same time
Better resource utilization: CPU idle time is reduced
Responsive applications: UI threads can remain active while background tasks run
Example Use Cases
Running background tasks in a desktop application
Handling multiple client requests in a server
Parallel processing of large data
2. Creating Threads in Java
Java provides two main ways to create threads:
A. Extend the Thread Class
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Start a new thread
}
}
B. Implement the Runnable Interface
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable Thread: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
Difference:
Thread class: Can’t extend any other class
Runnable interface: Allows extending another class
3. Thread Lifecycle
A thread in Java goes through several states:
New – Thread object is created
Runnable – Thread is ready to run
Running – Thread is executing
Waiting/Blocked – Thread waits for a resource
Timed Waiting – Thread waits for a specific time
Terminated – Thread has finished execution
4. Thread Methods in Java
start() – Starts the thread
run() – Contains code to execute
sleep(milliseconds) – Pauses thread
join() – Waits for another thread to finish
yield() – Temporarily pauses to let other threads run
setPriority() – Sets thread priority
5. Synchronization and Concurrency
When multiple threads access shared resources, race conditions can occur. Java provides tools to manage concurrency safely.
A. Synchronization
Prevents multiple threads from accessing a critical section simultaneously:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + counter.getCount());
}
}
B. Locks and ReentrantLock
Advanced alternative to synchronized:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
C. Volatile Variables
Used when multiple threads read/write a variable:
class SharedResource {
public volatile boolean flag = true;
}
Ensures visibility across threads
Doesn’t guarantee atomicity
6. Executor Framework (Recommended for Modern Java)
Instead of creating threads manually, use ExecutorService for thread pools:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
});
}
executor.shutdown(); // Stop accepting new tasks
}
}
Benefits:
Reuses threads (performance)
Manages task queue automatically
Scales efficiently
7. Common Concurrency Issues
Race Conditions – Two threads modify shared data simultaneously
Deadlock – Two or more threads wait for each other forever
Starvation – Low-priority thread never gets CPU time
Livelock – Threads keep reacting to each other but make no progress
8. Java Concurrency Utilities
Java provides high-level APIs in java.util.concurrent:
ExecutorService – Thread pools and task management
CountDownLatch – Waits for multiple threads to finish
Semaphore – Limits concurrent access to resources
CyclicBarrier – Threads wait for each other at a barrier
ConcurrentHashMap – Thread-safe map
BlockingQueue – Thread-safe queues
9. Best Practices
Prefer ExecutorService over manually creating threads
Minimize shared mutable state
Use synchronized or Locks for shared resources
Avoid blocking the main thread
Use volatile for simple flags
Handle exceptions inside threads
Monitor and tune thread pool sizes
10. Summary
Multithreading enables concurrent execution in Java
Concurrency ensures safe, coordinated access to shared resources
Key tools: Thread, Runnable, synchronized, Lock, ExecutorService
Modern Java favors ExecutorService and high-level concurrency utilities
Proper design avoids race conditions, deadlocks, and starvation
Multithreading and concurrency are essential for high-performance, scalable, and responsive Java applications.
Learn Full Stack JAVA Course in Hyderabad
Read More
Java 8 Features: Lambdas, Streams, and Functional Interfaces
Java Collections Framework: List, Set, Map
Java Data Types and Variables Explained
Visit Our Quality Thought Institute in Hyderabad
Subscribe by Email
Follow Updates Articles from This Blog via Email
No Comments