Java Multithreading & Concurrency

Java Multithreading & Concurrency
Multithreading in Java allows multiple threads to run concurrently, improving performance by utilizing CPU cores efficiently. It is widely used in applications requiring parallel execution, such as gaming, web servers, and real-time systems.
1. What is a Thread?
A thread is the smallest unit of execution in a program. Java supports multithreading via the Thread class and Runnable interface.
Single-threaded vs Multi-threaded Execution
Single-threaded Multi-threaded
Executes one task at a time Executes multiple tasks in parallel
Blocks execution for long-running tasks Runs tasks simultaneously, improving responsiveness
Suitable for simple applications Suitable for real-time systems, GUI applications, etc.
2. Creating Threads in Java
2.1 Using the Thread Class
✅ Example: Extending the Thread Class
class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Count: " + i);
try {
Thread.sleep(500); // Pause for 500ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Count: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start();
thread2.start();
}
}
class MyTask extends Thread {
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName() + " - Task " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadJoinExample {
public static void main(String[] args) {
MyTask thread1 = new MyTask();
MyTask thread2 = new MyTask();
thread1.start();
try {
thread1.join(); // Wait for thread1 to finish before starting thread2
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
class Counter {
private int count = 0;
public synchronized void increment() { // Synchronized method
count++;
}
public int getCount() {
return count;
}
}
public class SyncExample {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
class SharedResource {
public void printNumbers() {
synchronized (this) { // Synchronized block
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class SyncBlockExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread t1 = new Thread(() -> resource.printNumbers());
Thread t2 = new Thread(() -> resource.printNumbers());
t1.start();
t2.start();
}
}
class Resource {
void methodA(Resource r) {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " locked methodA");
synchronized (r) {
System.out.println(Thread.currentThread().getName() + " locked methodB");
}
}
}
}
public class DeadlockExample {
public static void main(String[] args) {
Resource r1 = new Resource();
Resource r2 = new Resource();
Thread t1 = new Thread(() -> r1.methodA(r2));
Thread t2 = new Thread(() -> r2.methodA(r1));
t1.start();
t2.start();
}
}