Java Multithreading: Creating, Managing, and Synchronizing Threads

Markdown

View as Markdown

Modern applications often perform multiple tasks simultaneously, such as processing user requests, downloading files, updating databases, or running background operations. Java supports these capabilities through multithreading, which allows multiple threads to execute concurrently within a program.

Multithreading improves application performance, responsiveness, and resource utilization. In this article, you'll learn how to create and manage threads in Java, understand the thread life cycle, and implement synchronization techniques for thread-safe programming.

Learning Objectives

  • Understand the concept of threads and multithreading.

  • Explain the Java thread life cycle.

  • Create threads using the Thread class.

  • Create threads using the Runnable interface.

  • Use commonly used thread methods.

  • Implement synchronization techniques.

  • Understand real-world applications of multithreading.

What is a Thread?

A thread is the smallest unit of execution within a program. Multiple threads can execute simultaneously, allowing applications to perform several tasks concurrently.

Examples of multithreading include:

  • Web browsers downloading files while displaying web pages.

  • Banking applications processing multiple transactions simultaneously.

  • Music applications playing songs while loading playlists.

  • Software applications running background tasks without interrupting users.

Java provides built-in support for multithreading through the Thread class and the Runnable interface.

 

What is Multithreading?

Multithreading is the process of executing multiple threads concurrently within a single application.

Benefits of Multithreading

  • Improved application performance

  • Better responsiveness

  • Efficient CPU utilization

  • Simultaneous execution of multiple tasks

  • Improved scalability for enterprise applications

Thread Life Cycle

A thread passes through multiple stages during its execution.

The major stages include:

  • New

  • Runnable

  • Running

  • Blocked / Waiting

  • Terminated

Description of Each Stage

State

Description

New

The thread is created but not yet started.

Runnable

The thread is ready for execution.

Running

The thread is currently executing.

Blocked / Waiting

The thread is temporarily paused or waiting for resources.

Terminated

The thread has completed its execution.

Understanding the thread life cycle helps developers manage thread execution efficiently.

Creating a Thread Using the Thread Class

One way to create a thread is by extending the Thread class.

Example

class MyThread extends Thread {

    public void run() {

        System.out.println(

                "Thread is running.");

    }

}

public class ThreadExample {

    public static void main(String[] args) {

        MyThread thread =

                new MyThread();

        thread.start();

    }

}

Output

Thread is running.

The start() method creates a new thread and automatically invokes the run() method.

Note: Always use start() instead of calling run() directly when creating threads.

 

 

Creating Multiple Threads

Java allows multiple threads to execute concurrently.

Example

class FirstThread extends Thread {

    public void run() {

        System.out.println("First Thread");

    }

}

class SecondThread extends Thread {

    public void run() {

        System.out.println("Second Thread");

    }

}

public class Main {

    public static void main(String[] args) {

        FirstThread thread1 =

                new FirstThread();

        SecondThread thread2 =

                new SecondThread();

        thread1.start();

        thread2.start();

    }

}

The output order may vary because both threads execute independently.

 

Creating a Thread Using the Runnable Interface

Implementing the Runnable interface is generally considered a better practice because it allows Java programs to inherit from other classes when required.

Example

class MyRunnable implements Runnable {

    public void run() {

        System.out.println(

                "Runnable Thread Executing");

    }

}

public class RunnableExample {

    public static void main(String[] args) {

        Thread thread =

                new Thread(new MyRunnable());

        thread.start();

    }

}

Output

Runnable Thread Executing

Using Runnable promotes better flexibility and code reusability.

 

 

Thread Methods

Java provides several useful methods for managing threads.

Method

Purpose

start()

Starts thread execution

run()

Contains thread logic

sleep()

Pauses thread execution temporarily

join()

Waits for another thread to complete

isAlive()

Checks whether a thread is active

getName()

Returns the thread name

setName()

Changes the thread name

currentThread()

Returns the currently executing thread

 

Using sleep()

The sleep() method temporarily pauses thread execution.

Example

public class SleepExample {

    public static void main(String[] args)

            throws InterruptedException {

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

            System.out.println(i);

            Thread.sleep(1000);

        }

    }

}

Output

1

2

3

4

5

The program pauses for one second between each iteration.

 

Using join()

The join() method waits for one thread to finish before another thread continues execution.

Example

class Task extends Thread {

    public void run() {

        System.out.println(

                "Task Completed");

    }

}

public class JoinExample {

    public static void main(String[] args)

            throws Exception {

        Task thread =

                new Task();

        thread.start();

 

        thread.join();

        System.out.println(

                "Main Method Completed");

    }

}

Output

Task Completed

Main Method Completed

Synchronization in Java

When multiple threads access shared resources simultaneously, inconsistent results may occur. Synchronization ensures that only one thread accesses critical sections of code at a time.

Synchronization helps prevent:

  • Data inconsistency

  • Race conditions

  • Unexpected application behavior

Example Without Synchronization

class Counter {

    int count = 0;

    public void increment() {

        count++;

    }

}

If multiple threads update the value simultaneously, the final result may become inconsistent.

 

 Using Synchronized Methods

Example

class Counter {

    int count = 0;

    public synchronized void increment() {

        count++;

    }

}

The synchronized keyword allows only one thread to execute the method at a time.

Synchronizing Blocks of Code

Synchronization can also be applied to specific code blocks.

Example

public void display() {

    synchronized(this) {

        System.out.println(

                "Thread Safe Operation");

    }

}

Synchronizing only the required code improves application performance.

 

Thread Safety

A program is considered thread-safe when shared resources are accessed correctly without causing unexpected behavior.

Thread safety becomes particularly important in:

  • Banking applications

  • Financial systems

  • Inventory management systems

  • Enterprise applications

  • Database-driven applications

Synchronization plays a critical role in ensuring thread safety.

Real-World Applications

Multithreading is widely used in:

  • Banking applications

  • Web servers

  • Gaming applications

  • Operating systems

  • File download managers

  • Video streaming platforms

  • Enterprise software systems

  • Database applications

Examples include:

  • Processing multiple customer requests simultaneously.

  • Running background tasks while users interact with an application.

  • Executing scheduled operations without interrupting system performance.

 

Common Mistakes

Avoid these common errors when working with threads:

  • Calling run() directly instead of start().

  • Ignoring synchronization requirements.

  • Creating unnecessary threads that consume system resources.

  • Forgetting to handle InterruptedException.

  • Accessing shared resources without proper synchronization.

  • Assuming that thread execution always follows a fixed order.

Practice Exercise

Write Java programs to:

  1. Create a thread by extending the Thread class.

  2. Create a thread using the Runnable interface.

  3. Execute multiple threads simultaneously.

  4. Pause thread execution using sleep().

  5. Wait for thread completion using join().

  6. Implement synchronized methods.

  7. Demonstrate thread-safe access to shared resources.

  8. Display thread names using getName().

 

 

Was this article helpful?

Still need help?

Contact us