Search code examples
javawaitsynchronizedjava.util.concurrentnotify

LeetCode 1116 Java Concurrency Issue, will waiting thread revisit code before?


Here is my code for the problem which works fine, however, when I change the while statement right under synchronized statement, it seems any thread that get the lock after notifyall() just continue to run the code and causing odd method accept even number, even method accept odd number.

class ZeroEvenOdd {
    private int n;
    private AtomicInteger counter;
    private AtomicInteger mode;
    private Object lock;
    
    public ZeroEvenOdd(int n) {
        this.n = n;
        this.counter = new AtomicInteger(0);
        this.mode = new AtomicInteger(0);
        this.lock = new Object();

    }

    public void zero(IntConsumer printNumber) throws InterruptedException {
        while(counter.get() < n){
            synchronized(lock){
                // if I change this line to 
                // if(mode.get() == 1 || mode.get() == 3){lock.wait();}
                // then everything goes wrong
                while(mode.get() == 1 || mode.get() == 3){lock.wait();}

                if(counter.get() < n){
                    printNumber.accept(0);}
                    mode.incrementAndGet();
                    lock.notifyAll();
            }
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        while(counter.get() < n){
            synchronized(lock){
                 // if I change this line to 
                // if(mode.get() != 3){lock.wait();}
                // then everything goes wrong
                while(mode.get() != 3){lock.wait();}
                if(counter.get() < n){
                    printNumber.accept(counter.incrementAndGet());}
                mode.set(0);
                lock.notifyAll();
            }
            
        }
        
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        while(counter.get() < n){
            synchronized(lock){
                 // if I change this line to 
                // if(mode.get() != 1){lock.wait();}
                // then everything goes wrong
                while(mode.get() != 1){lock.wait();}
                if(counter.get() < n){
                    printNumber.accept(counter.incrementAndGet());}
                mode.incrementAndGet();
                lock.notifyAll();
                
            }

        }
        
    }
}

It seems to me that, when I use while, whichever thread got the lock after notifyAll will check the mode again, but when I change to if, it will not check mode and just continue from where it left.

I searched online, after notifyAll(), the next thread get the lock will continue from where it left, it wont revisit the code before wait().

But by using while here, it strangely check the mode variable again.


Solution

  • while is a looping construct.

    It is not that the thread revisits the code before - it just loops again:

    while (mode.get() != 1) {
        lock.wait();
    }
    

    After the wait() call finishes the loop condition is evaluated again in the same way that

    while (counter.get() < n) {
        // some code here that might modify the value of the counter
    }
    

    loops for as long as counter.get() returns a value less than n.

    The other approach (using if):

    if (mode.get() != 3) {
        lock.wait();
    }
    

    only calls lock.wait(); once if mode.get() returns something else then 3. After lock.wait(); returns it just continues with the code after the if statement, no matter what mode.get() would return.