Java Threads

Concurrent programming and multithreading

๐Ÿงต What are Threads?

Threads allow concurrent execution of multiple parts of a program. They enable multitasking within a single application, improving performance and responsiveness.


// Simple thread creation
Thread thread = new Thread(() -> {
    System.out.println("Running in thread: " + Thread.currentThread().getName());
});
thread.start();
                                    

Output:

Running in thread: Thread-0

Thread executes concurrently with main program

Thread Concepts

๐Ÿƒ

Thread Class

Extend Thread class

class MyThread extends Thread {
    public void run() { }
}
โš™๏ธ

Runnable Interface

Implement Runnable interface

class Task implements Runnable {
    public void run() { }
}
๐Ÿ”„

Synchronization

Thread-safe operations

synchronized void method() {
    // Thread-safe code
}
๐ŸŽฏ

Thread Pool

Manage multiple threads

ExecutorService executor = 
    Executors.newFixedThreadPool(5);

๐Ÿ”น Creating Threads

Two main ways to create threads in Java:

// Method 1: Extending Thread class
class MyThread extends Thread {
    private String threadName;
    
    public MyThread(String name) {
        this.threadName = name;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(threadName + " - Count: " + i);
            try {
                Thread.sleep(1000); // Sleep for 1 second
            } catch (InterruptedException e) {
                System.out.println(threadName + " interrupted");
            }
        }
    }
}

// Method 2: Implementing Runnable interface
class MyTask implements Runnable {
    private String taskName;
    
    public MyTask(String name) {
        this.taskName = name;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(taskName + " - Step: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println(taskName + " interrupted");
            }
        }
    }
}

// Usage
public class ThreadExample {
    public static void main(String[] args) {
        // Using Thread class
        MyThread thread1 = new MyThread("Worker-1");
        thread1.start();
        
        // Using Runnable interface
        Thread thread2 = new Thread(new MyTask("Task-1"));
        thread2.start();
    }
}

Output:

Worker-1 - Count: 1

Task-1 - Step: 1

Task-1 - Step: 2

Worker-1 - Count: 2

... (concurrent execution)

๐Ÿ”น Thread Synchronization

Preventing race conditions with synchronized methods:

class Counter {
    private int count = 0;
    
    // Synchronized method - thread-safe
    public synchronized void increment() {
        count++;
        System.out.println(Thread.currentThread().getName() + 
                          " - Count: " + count);
    }
    
    public synchronized int getCount() {
        return count;
    }
}

class CounterThread extends Thread {
    private Counter counter;
    private String threadName;
    
    public CounterThread(Counter counter, String name) {
        this.counter = counter;
        this.threadName = name;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            counter.increment();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                System.out.println(threadName + " interrupted");
            }
        }
    }
}

// Usage
public class SynchronizationExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        
        CounterThread t1 = new CounterThread(counter, "Thread-1");
        CounterThread t2 = new CounterThread(counter, "Thread-2");
        
        t1.start();
        t2.start();
        
        // Wait for threads to complete
        t1.join();
        t2.join();
        
        System.out.println("Final count: " + counter.getCount());
    }
}

Output:

Thread-1 - Count: 1

Thread-2 - Count: 2

Thread-1 - Count: 3

... (synchronized execution)

Final count: 10

๐Ÿ”น Thread Pool with ExecutorService

Managing multiple threads efficiently:

import java.util.concurrent.*;

class WorkerTask implements Runnable {
    private int taskId;
    
    public WorkerTask(int id) {
        this.taskId = id;
    }
    
    @Override
    public void run() {
        System.out.println("Task " + taskId + " started by " + 
                          Thread.currentThread().getName());
        try {
            // Simulate work
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.out.println("Task " + taskId + " interrupted");
        }
        System.out.println("Task " + taskId + " completed");
    }
}

public class ThreadPoolExample {
    public static void main(String[] args) {
        // Create thread pool with 3 threads
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // Submit 6 tasks
        for (int i = 1; i <= 6; i++) {
            executor.submit(new WorkerTask(i));
        }
        
        // Shutdown executor
        executor.shutdown();
        
        try {
            // Wait for all tasks to complete
            if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
        
        System.out.println("All tasks completed");
    }
}

Output:

Task 1 started by pool-1-thread-1

Task 2 started by pool-1-thread-2

Task 3 started by pool-1-thread-3

Task 1 completed

Task 4 started by pool-1-thread-1

... (managed execution)

๐Ÿง  Test Your Knowledge

What is the purpose of the synchronized keyword?