Understanding Java Multithreading with Practical Examples

In this guide, we will explore Java multithreading, a powerful feature that allows concurrent execution of two or more threads. We'll cover key concepts and provide practical code snippets to help you grasp the fundamentals of multithreading in Java.
By Jamie

What is Multithreading?

Multithreading is a programming concept in which multiple threads are executed simultaneously, allowing for more efficient use of CPU resources. In Java, threads can be created by implementing the Runnable interface or extending the Thread class.

Example 1: Creating a Thread Using the Thread Class

Here’s a simple example of creating a thread by extending the Thread class:

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // Start the thread
    }
}

Example 2: Creating a Thread Using the Runnable Interface

Alternatively, you can create a thread by implementing the Runnable interface:

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable thread is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start(); // Start the thread
    }
}

Example 3: Synchronization in Java

When multiple threads access shared resources, synchronization is essential to prevent data inconsistency. Here’s an example of using the synchronized keyword:

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();

        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();
        t1.join();
        t2.join();

        System.out.println("Count: " + counter.getCount()); // Output will be 2000
    }
}

Example 4: Using ExecutorService for Thread Pooling

Java provides the ExecutorService framework to manage a pool of threads, making it easier to handle multiple threads:

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++) {
            executor.submit(() -> {
                System.out.println("Thread " + Thread.currentThread().getName() + " is executing.");
            });
        }

        executor.shutdown(); // Initiates an orderly shutdown
    }
}

Conclusion

Java multithreading is a crucial aspect of modern programming that improves application performance. By utilizing the examples above, you can begin to incorporate multithreading into your Java applications effectively.