0

I am trying to do something like this:

There is a class Q which has a field called n and two methods put() and get() which sets the value of n or retrieves the value of n. And then there are two classes Producer and Consumer. Producer class has a thread which calls put and consumer class has a thread which calls get. I am trying to synchronize it using an Object lock which is the only instance of Singleton class LockClass.

So, here is my class Q :

public class Q {

    int n;

    public void put(int n){
        System.out.println("Put " + n);
        this.n = n;
    }

    public int get(){
        System.out.println("Got " + n);
        return n;
    }
}

LockClass:

 public class LockClass {

        private static  Object Lock = new Object();

        private LockClass(){

        }


        public static Object getLock(){
            return Lock;
        }
    }

Consumer:

public class Consumer implements Runnable {

    Thread t;
    Q q;


    public Consumer(Q q){
        this.q = q;
        t = new Thread(this);
        t.start();
    }
    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){

            synchronized(LockClass.getLock()){
                q.get();
            }
            try {
                System.out.println("Consumer slept");
                Thread.sleep(1000);
                System.out.println("Consumer awake");

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

Producer:

public class Producer implements Runnable {

    Q q;
    Thread t;
    int i;

    public Producer(Q q){
        this.q = q;
        t = new Thread(this);
        t.start();
        i = 0;
    }
    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true){
            i++;
            synchronized(LockClass.getLock()){
                q.put(i);
            }
            try {
                System.out.println("Producer slept");
                Thread.sleep(1000);
                System.out.println("Producer awake");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


}

DemoClass : This class has the main function

public class DemoClass {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Q q = new Q();

        Producer prod = new Producer(q);
        Consumer cons = new Consumer(q);
    }

}

So, when I run this above code, I get something like this :

Put 1
Producer slept
Got 1
Consumer slept
Consumer awake
Producer awake
Got 1
Consumer slept
Put 2
Producer slept
Consumer awake
Producer awake
Got 2
Consumer slept
Put 3
Producer slept
Consumer awake
Producer awake
Got 3
Consumer slept
Put 4
Producer slept

So, I am actually making the thread sleep in both producer and consumer so that there is enough time for context switching. Now when both needs to sleep for same time, if producer sleeps first, it should get awake first . But as I can see in output, the producer sleeps first but still consumer awakes first and as whichever thread gets awake first should get the lock and so another thread should wait till the lock is released.

Why is it not working the way I am expecting? Am I missing something?

Priyansh Goel
  • 2,660
  • 1
  • 13
  • 37
  • 1
    Whenever you hear "producer" and "consumer", the first thing you should think of is a `BlockingQueue`. The producer loops putting objects into the queue (the objects could be `Integer` objects in this case), and the consumer loops taking objects out. If the queue is empty when the consumer tries to remove an object, the consumer will be blocked until an object becomes available. And, If the queue has an upper size limit (often a good idea), then the producer will be blocked whenever the queue becomes full. – Solomon Slow Apr 28 '16 at 14:56
  • @jameslarge : Thank you so much! :) Will look into a BlockingQueue – Priyansh Goel Apr 28 '16 at 17:54

2 Answers2

2

From the doc:

... However, these sleep times are not guaranteed to be precise, because they are limited by the facilities provided by the underlying OS... In any case, you cannot assume that invoking sleep will suspend the thread for precisely the time period specified

https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html

Y.E.
  • 902
  • 7
  • 14
1

The sleep timeout is not guaranteed to be strict, so it's absolutely valid that one thread can sleep later and wake earlier. Quotation from Thread.sleep java doc:

[...] subject to the precision and accuracy of system timers and schedulers

Theoretically it's even possible for one thread to make the action twice, while second thread will be sleeping.

If you want two threads to act one after another, use wait-notify mechanism:

producer

Object lock = LockClass.getLock();
synchronized(lock){
    q.put(i);
    lock.notifyAll();
    lock.wait();    
}

consumer

Object lock = LockClass.getLock();
synchronized(lock){
    q.get(i);
    lock.notifyAll();
    lock.wait();    
}
AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • So, I was actually trying to use the same functionality using the single lock object without wait, notify and notifyall . So if both threads sleep for same time, and suppose thread 1 sleeps earlier than thread 2, then in the meanwhile thread2 will go to sleep (suppose it sleeps after x ms). Now when thread1 will awake, the JVM will give control to the thread1 as no other thread is there. Meanwhile thread 2 will be awake and should wait as the lock has been taken by thread1. And this should go on.. Am I wrong in this understanding? – Priyansh Goel Apr 28 '16 at 09:09
  • @PriyanshGoel if you wan't to use sleep, try to make a gap between two threads, for example start second thread 500ms later than first thread – AdamSkywalker Apr 28 '16 at 09:48
  • -1 for inviting a newbie to use the wait()/notify()/notifyAll() mechanism in a way that they were not meant to be used. Wait/notify is a major source of confusion for noobs on this site. It's a low-level primitive that is meant to be used [in a very specific way](https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html) to implement higher-level synchronization objects. Most problems (including this one) can be solved using the higher-level objects that are provided by the standard library. (E.g., `BlockingQueue` would work well in this case.) – Solomon Slow Apr 28 '16 at 14:49