Multithreading in Java

by Jasleen Chhabra | Updated on 24 August 2024

Table of Contents (Click any Topic to view first)

  1. Multithreading in Java
  2. Thread Creation and Management
  3. Synchronization and Thread Safety
  4. Concurrency Utilities

Multithreading in Java

Multithreading is a Java feature that allows the concurrent execution of two or more parts of a program for maximum utilization of CPU. Each part of such a program is called a thread. Threads are lightweight processes.

1. Thread Creation and Management

In Java, there are two main ways to create a thread:

  1. By extending the Thread class

  2. By implementing the Runnable interface

Extending the Thread Class

When a class extends the Thread class, it should override the run() method to define the code that constitutes the new thread's task.

Example:

class MyThread extends Thread {

    @Override

    public void run() {

        System.out.println("Thread is running.");

    }

    

    public static void main(String[] args) {

        MyThread thread = new MyThread();

        thread.start(); // Start the thread

    }

}

Implementing the Runnable Interface

This approach is more flexible because the class can extend another class as well.

Example:

java

Copy code

class MyRunnable implements Runnable {

    @Override

    public void run() {

        System.out.println("Thread is running.");

    }

    

    public static void main(String[] args) {

        MyRunnable runnable = new MyRunnable();

        Thread thread = new Thread(runnable);

        thread.start(); // Start the thread

    }

}


2. Synchronization and Thread Safety

Synchronization is the capability to control the access of multiple threads to shared resources. Without synchronization, it is possible for one thread to modify a shared object while another thread is in the process of using or updating the object's value.

Synchronized Methods

A synchronized method ensures that only one thread can execute it at a time.

Example:

class Table {

    synchronized void printTable(int n) { // Synchronized method

        for (int i = 1; i <= 5; i++) {

            System.out.println(n * i);

            try {

                Thread.sleep(400);

            } catch (InterruptedException e) {

                System.out.println(e);

            }

        }

    }

}

 

class MyThread1 extends Thread {

    Table t;

    MyThread1(Table t) {

        this.t = t;

    }

    

    public void run() {

        t.printTable(5);

    }

}

 

class MyThread2 extends Thread {

    Table t;

    MyThread2(Table t) {

        this.t = t;

    }

    

    public void run() {

        t.printTable(100);

    }

}

 

public class TestSynchronization {

    public static void main(String args[]) {

        Table obj = new Table(); // Only one object

        MyThread1 t1 = new MyThread1(obj);

        MyThread2 t2 = new MyThread2(obj);

        t1.start();

        t2.start();

    }

}


Synchronized Blocks

To synchronize only a specific block of code instead of the entire method, use synchronized blocks.

Example:

class Table {

    void printTable(int n) {

        synchronized (this) { // Synchronized block

            for (int i = 1; i <= 5; i++) {

                System.out.println(n * i);

                try {

                    Thread.sleep(400);

                } catch (InterruptedException e) {

                    System.out.println(e);

                }

            }

        }

    }

}

 

class MyThread1 extends Thread {

    Table t;

    MyThread1(Table t) {

        this.t = t;

    }

    

    public void run() {

        t.printTable(5);

    }

}

 

class MyThread2 extends Thread {

    Table t;

    MyThread2(Table t) {

        this.t = t;

    }

    

    public void run() {

        t.printTable(100);

    }

}

 

public class TestSynchronizationBlock {

    public static void main(String args[]) {

        Table obj = new Table(); // Only one object

        MyThread1 t1 = new MyThread1(obj);

        MyThread2 t2 = new MyThread2(obj);

        t1.start();

        t2.start();

    }

}


3. Concurrency Utilities

The java.util.concurrent package provides a comprehensive set of classes for managing concurrent programming, making it easier to write efficient, thread-safe, and scalable code.

Executor Framework

The Executor framework helps in decoupling task submission from task execution. It provides a pool of threads to execute tasks.

Example:

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

class WorkerThread implements Runnable {

    private String message;

    

    public WorkerThread(String s) {

        this.message = s;

    }

    

    @Override

    public void run() {

        System.out.println(Thread.currentThread().getName() + " (Start) message = " + message);

        processMessage();

        System.out.println(Thread.currentThread().getName() + " (End)");

    }

    

    private void processMessage() {

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

 

public class TestThreadPool {

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(5); // Creating a pool of 5 threads

        for (int i = 0; i < 10; i++) {

            Runnable worker = new WorkerThread("" + i);

            executor.execute(worker); // Calling execute method of ExecutorService

        }

        executor.shutdown();

        while (!executor.isTerminated()) {

        }

        System.out.println("Finished all threads");

    }

}

Other Concurrency Utilities

  • Locks: More flexible and sophisticated thread synchronization compared to synchronized methods and blocks.

  • CountDownLatch: A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

  • CyclicBarrier: A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

  • Semaphore: A counting semaphore that maintains a set of permits for controlling access to a resource.

  • Concurrent Collections: Thread-safe versions of standard collections, such as ConcurrentHashMap, CopyOnWriteArrayList, and BlockingQueue.

Example of Concurrent Collection:

import java.util.concurrent.ConcurrentHashMap;

 

public class ConcurrentMapExample {

    public static void main(String[] args) {

        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        map.put("A", 1);

        map.put("B", 2);

        map.put("C", 3);

 

        map.forEach((key, value) -> System.out.println(key + ": " + value));

 

        map.putIfAbsent("A", 4); // This will not change the value of "A"

        map.putIfAbsent("D", 5); // This will add "D" to the map

 

        System.out.println("After putIfAbsent operations:");

        map.forEach((key, value) -> System.out.println(key + ": " + value));

    }

}

Conclusion

Multithreading in Java is a powerful feature that allows for concurrent execution of code, improving performance and resource utilization. By understanding thread creation and management, synchronization techniques, and the concurrency utilities provided by Java, you can write robust, efficient, and scalable concurrent applications.



FAQ

Any Questions?
Look Here.

Related Articles

Abstraction in Java

Binding in Java

Break Statement in Java

Classes & Objects in Java

Collections Framework in Java

Comments in Java

Continue Statement in Java

Control Statements in Java

Data Types in Java

Do While-Loop in Java

Encapsulation in Java

Exception Handling in Java

For-Loop In Java

Hello World Program in Java

If-else Statement in Java

Inheritance in Java

Introduction to Java

Java Database Connectivity (JDBC)

Java Development Tools and Frameworks

Java GUI (Graphical User Interface) Programming

Java I/O

Java Vs. C++

Methods and Constructors in Java

Object Oriented Programming in Java

Operators in Java

Polymorphism in Java

Scanner Class in Java

Setting Up Java Environment

Static Keyword in Java

Super Keyword in Java

Switch Statement in Java

This Keyword in Java

Variables in Java

While-Loop in Java