Java - Inter-thread Communication

Welcome, aspiring Java programmers! Today, we're going to embark on an exciting journey into the world of inter-thread communication in Java. As your friendly neighborhood computer science teacher, I'm here to guide you through this fascinating topic. So, grab your favorite beverage, get comfortable, and let's dive in!

Java - Inter-thread Communication

What is Inter-thread Communication?

Imagine you're in a relay race. You have to pass the baton to your teammate at just the right moment. That's essentially what inter-thread communication is all about in the programming world. It's how different threads in a Java program talk to each other, coordinate their actions, and share information.

Why is Inter-thread Communication Important?

Inter-thread communication is crucial for creating efficient, synchronized programs. Without it, our threads would be like runners in separate races, unable to cooperate or share resources effectively.

Methods Used for Inter-thread Communication

Java provides several methods for inter-thread communication. Let's look at them in a handy table:

Method Description
wait() Causes the current thread to wait until another thread invokes the notify() or notifyAll() methods for this object
notify() Wakes up a single thread that is waiting on this object's monitor
notifyAll() Wakes up all threads that are waiting on this object's monitor

Now, let's break these down and see how they work in action!

The wait() Method

The wait() method is like telling a thread, "Hey, take a break until someone gives you a nudge." Here's how it works:

synchronized(object) {
    while(condition) {
        object.wait();
    }
}

In this code:

  1. We first synchronize on an object to ensure thread safety.
  2. We check a condition in a while loop.
  3. If the condition is true, we call wait(), which makes the thread pause and wait for notification.

The notify() Method

The notify() method is like tapping a waiting thread on the shoulder and saying, "Wake up! It's your turn now." Here's how we use it:

synchronized(object) {
    // Change the condition
    condition = true;
    object.notify();
}

In this code:

  1. We synchronize on the same object as in the wait() call.
  2. We change the condition that the waiting thread was checking.
  3. We call notify() to wake up one waiting thread.

The notifyAll() Method

The notifyAll() method is like shouting, "Everyone wake up!" It's used when you want to alert all waiting threads. Here's an example:

synchronized(object) {
    // Change the condition
    condition = true;
    object.notifyAll();
}

This works similarly to notify(), but wakes up all waiting threads instead of just one.

A Real-world Example: The Producer-Consumer Problem

Let's put all this together with a classic example: the Producer-Consumer problem. Imagine a bakery where one person (the producer) makes bread, and another (the consumer) sells it. They share a limited shelf space.

Here's how we can implement this in Java:

class Bakery {
    private int breads = 0;
    private final int CAPACITY = 5;

    public synchronized void produceBread() throws InterruptedException {
        while (breads == CAPACITY) {
            System.out.println("Shelf is full! Baker is waiting...");
            wait();
        }
        breads++;
        System.out.println("Baker made a bread. Total: " + breads);
        notify();
    }

    public synchronized void sellBread() throws InterruptedException {
        while (breads == 0) {
            System.out.println("No bread! Seller is waiting...");
            wait();
        }
        breads--;
        System.out.println("Sold a bread. Remaining: " + breads);
        notify();
    }
}

class Baker implements Runnable {
    private Bakery bakery;

    public Baker(Bakery bakery) {
        this.bakery = bakery;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                bakery.produceBread();
                Thread.sleep(1000); // Time to bake
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Seller implements Runnable {
    private Bakery bakery;

    public Seller(Bakery bakery) {
        this.bakery = bakery;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                bakery.sellBread();
                Thread.sleep(1500); // Time between sales
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class BakeryDemo {
    public static void main(String[] args) {
        Bakery bakery = new Bakery();
        Thread baker = new Thread(new Baker(bakery));
        Thread seller = new Thread(new Seller(bakery));

        baker.start();
        seller.start();
    }
}

Let's break this down:

  1. We have a Bakery class that manages the bread inventory.
  2. The produceBread() method represents the baker making bread. If the shelf is full, the baker waits.
  3. The sellBread() method represents the seller selling bread. If there's no bread, the seller waits.
  4. We use wait() when the conditions aren't right for producing or selling.
  5. We use notify() after producing or selling to alert the other thread.
  6. The Baker and Seller classes run in separate threads, continuously trying to produce or sell bread.

When you run this program, you'll see the baker and seller working together, waiting when necessary, and notifying each other when they can proceed. It's like watching a well-coordinated dance!

Conclusion

And there you have it, folks! We've journeyed through the land of inter-thread communication in Java. We've seen how threads can coordinate their actions using wait(), notify(), and notifyAll(). We've even built a virtual bakery to see these concepts in action!

Remember, like in our bakery example, good inter-thread communication is all about balance and coordination. It's about knowing when to work, when to wait, and when to signal others. Master this, and you'll be well on your way to creating efficient, well-coordinated Java programs.

Keep practicing, stay curious, and happy coding! And remember, in programming as in life, good communication is key to success. Until next time, this is your friendly neighborhood computer science teacher, signing off!

Credits: Image by storyset