Search code examples
javajava-threadsthread-synchronization

Why is the code just after wait method not invoked


I have a customer account class with below 2 methods, addBalance and deductBalance.

class CustomerAccount
{
    
    private int balance;
    int getBalance() { return balance;}
    CustomerAccount() {balance=0;}
    boolean deductBalance(final int amount)
    {
        System.out.println("> invoked to deduct :" + amount);
        synchronized (this)
        {
            while (balance <= amount)
            {
                try {wait(); } catch (InterruptedException e) {TestWaitNotifyBasic.logException(e);}
                System.out.println("> hey I was notified for cutting amount:" + amount);
            }
            balance-= amount;
        }
        System.out.println("> deducted:" + amount);
        return true;
    }
    boolean addBalance(final int amount)
    {
        synchronized (this)
        {
            balance += amount;
            notifyAll();
        }
        System.out.println("> balance added: " + amount);
        return true;
    }
}

Below is the consuming class with main method. The idea is to have threads wait for synchronized block until notifyAll is called.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.IntFunction;



public class TestWaitNotifyBasic
{

    public static void main(String[] args)
    {
        final CustomerAccount chuck = new CustomerAccount();

        IntFunction<Runnable> addBalance = (amount) -> { return () -> chuck.addBalance(amount);};
        IntFunction<Runnable> deductBalance = (amount) -> { return () -> chuck.deductBalance(amount);};

        ExecutorService threadPool = Executors.newFixedThreadPool(100);

        //balance deduction
        threadPool.execute(deductBalance.apply(1000) );
        threadPool.execute(deductBalance.apply(50) );
        threadPool.execute(deductBalance.apply(5000));

        //balance addition
        threadPool.execute(addBalance.apply(900));
        threadPool.execute(addBalance.apply(40));
        threadPool.execute(addBalance.apply(80));
        threadPool.execute(addBalance.apply(8000));

        threadPool.shutdown();
        while (!threadPool.isTerminated())
        {
            try {Thread.sleep(300);} catch (InterruptedException e) {{logException(e);}}
        }
        System.out.println("----------------------" );
        System.out.println("thread operations finished, final balance : " + chuck.getBalance());
        int actualBalance = (900+40+80+8000)-(1000+50+5000);
        System.out.println("Validity check " + actualBalance);
    }

    static void logException(InterruptedException e)
    {
        System.out.println("###########interruptedexception#########" + e);
    }

}

Below is my console output

> balance added: 80
> invoked to deduct :1000
> balance added: 8000
> invoked to deduct :5000
> invoked to deduct :50
> balance added: 900
> balance added: 40
> deducted:5000
> deducted:50
> deducted:1000
----------------------
thread operations finished, final balance : 2970
Validity check 2970

The question I have is, why the below is not printed in console output

System.out.println("> hey I was notified for cutting amount:" + amount);

This method is written next to wait() of deduct balance method in CustomerAccount class.


Solution

  • Following @RealSkeptic's comment, modified the addBalance Thread to sleep before entering execution. Post this all the sysouts got executed

    The behavior of code posted above was that the addBalance threads would get executed before the deductBalance thread entered the synchronized block.

    With below method for addBalance, the ouptut changes.

    boolean addBalance(final int amount)
        {
            try {Thread.sleep(3000);} catch (InterruptedException e) {{TestWaitNotifyBasic.logException(e);}}
            synchronized (this)
            {
                balance += amount;
                notifyAll();
            }
            System.out.println("> balance added: " + amount);
            return true;
        }
    

    Console output post method change:

    > invoked to deduct :50
    > invoked to deduct :1000
    > invoked to deduct :5000
    > balance added: 900
    > balance added: 40
    > hey I was notified for cutting amount:50
    > balance added: 8000
    > balance added: 80
    > hey I was notified for cutting amount:5000
    > deducted:50
    > hey I was notified for cutting amount:1000
    > deducted:1000
    > deducted:5000
    
    ----------------------
    thread operations finished, final balance : 2970
    Validity check 2970