Search code examples
javamultithreadingsleepproducer-consumer

Producer Consumer multithreading why Thread.sleep is needed?


While I do understand the Gist of inter thread communication and the usage of wait and notify on the monitor to ensure Put/Get operations are synchronized - I'm trying to understand why we need the Thread.sleep() in the code below for both producer and consumer when we have a working wait/notify mechanism? If I remove the thread.sleep() - the output goes to hell!

import java.io.*;
import java.util.*;

public class Test {
    public static void main(String argv[]) throws Throwable {

        Holder h = new Holder();
        Thread p = new Thread(new Producer(h), "Producer");
        Thread c = new Thread(new Consumer(h), "Consumer");
        p.start();
        c.start();
    }
}

class Holder {
    int a;
    volatile boolean hasPut;

    public synchronized void put(int i) {
        while (hasPut) {
            try {
                System.out.println("The thread " + Thread.currentThread().getName() + " Going ta sleep..."); 
                wait(1000);
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
        this.a = i;
        hasPut = true;
        notifyAll();
    }

    public synchronized int get() {
        while (!hasPut) {
            try {
                System.out.println("The thread " + Thread.currentThread().getName() + " Going ta sleep..."); 
                wait(1000);
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
        hasPut = false;
        notifyAll();
        return this.a;
    }
}

class Producer implements Runnable {
    Holder h;
    public Producer(Holder h) {
        this.h = h;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("Putting : "+i); 
            h.put(i);
            try {
                Thread.sleep(10);
            } catch (InterruptedException ie) {
            }
        }
    }
}

class Consumer implements Runnable {
    Holder h;
    public Consumer(Holder h) {
        this.h = h;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            int k = h.get();
            System.out.println("Getting : "+k); 
            try {
                Thread.sleep(10);
            } catch (InterruptedException ie) {
            }
        }
    }
}

Solution

  • I think you get confused by the console output.

    The important part is if every .get() in the consumer gets all the elements from the producer. When you remove all the confusing System.out. lines and just use

    class Consumer implements Runnable {
        Holder h;
        public Consumer(Holder h) {
            this.h = h;
        }
    
        public void run() {
            for (int i = 0; i < 1000; i++) {
                int k = h.get();
                if (k != i)
                    System.out.println("Got wrong value " + k + "expected value " + i); 
            }
        }
    }
    

    You will see that your code works fine.

    I think your confusion comes from outputs that looks like this

    Getting : 990
    Putting : 993
    Getting : 991
    Getting : 992
    The thread Consumer Going ta sleep...
    Getting : 993
    

    But also you see all the gets are in the right order and all the puts too. So this is a problem of the way in which the output works in Java, when multiple threads are involved.