Threads in Java

Introduction
In modern applications, responsiveness and efficiency are essential. Whether you’re building a desktop app, a game, or a backend server, you often need to perform multiple tasks simultaneously. This is where Threads in Java come into play. Threads allow you to run multiple tasks concurrently, making programs faster and more efficient.

In this blog, we’ll explore:

  • What threads are and why they are important
  • How to create and manage threads in Java
  • The thread lifecycle
  • Common problems like race conditions
  • How to ensure thread safety

What is a Thread?

A thread is the smallest unit of execution in a program. While a process represents an entire running application, threads are lightweight sub-tasks inside that process.

👉 Example: When you use WhatsApp, one thread handles message sending, another handles receiving, while another manages notifications — all at the same time.

This is possible because of multithreading, which enables multiple threads to run concurrently in a single program.


Creating Threads in Java

Java provides two main ways to create threads:

1. Extending the Thread Class

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

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

2. Implementing the Runnable Interface

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

  public static void main(String[] args) {
    Thread t1 = new Thread(new MyRunnable());
    t1.start();
  }
}

✅ In both cases, calling .start() will invoke the run() method in a separate thread.


Managing Thread Execution

Sometimes, you want to control how threads run. Java provides useful methods for this:

  • sleep(ms) – pauses the thread for a given time.
  • join() – makes one thread wait until another finishes.
class Demo extends Thread {
  public void run() {
    for(int i=1; i<=5; i++) {
      try { Thread.sleep(500); } catch(Exception e) {}
      System.out.println(i);
    }
  }

  public static void main(String[] args) {
    Demo t1 = new Demo();
    Demo t2 = new Demo();
    t1.start();
    try { t1.join(); } catch(Exception e) {}
    t2.start();
  }
}

Here, t2 will start only after t1 finishes.


Thread Lifecycle

A thread doesn’t always run continuously. It passes through different states:

  1. New → Created but not started.
  2. Runnable → Ready to run but waiting for CPU.
  3. Running → Actively executing.
  4. Waiting/Timed Waiting → Paused temporarily (sleep() / wait()).
  5. Terminated → Finished execution.

Thread Safety and Synchronization

When multiple threads access a shared resource, problems can occur.

Example of Race Condition:

class Counter {
  int count = 0;

  public void increment() {
    count++;
  }
}

public class RaceConditionDemo {
  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("Final count: " + counter.count);
  }
}

👉 Expected result is 2000, but due to race conditions, you may get less.

Fix with Synchronization

class Counter {
  int count = 0;

  public synchronized void increment() {
    count++;
  }
}

The synchronized keyword ensures only one thread modifies count at a time.


Key Takeaways

  • Threads allow concurrent execution, improving efficiency.
  • You can create threads using Thread or Runnable.
  • Methods like sleep() and join() help manage thread execution.
  • Threads pass through different lifecycle states.
  • Synchronization is crucial to avoid race conditions and ensure thread safety.

Conclusion

Multithreading is a powerful feature in Java that makes applications more responsive and efficient. However, working with threads requires careful management to avoid race conditions and deadlocks. As you gain experience, you’ll learn how to balance performance with safety when writing multithreaded programs.

Comments

Popular posts from this blog

Spring Boot on AWS EC2: Upload to S3 Securely Using IAM Role

Java Streams Intermediate Operations Explained with Examples

ConcurrentHashMap vs Synchronized HashMap in Java