0

I was studying MultiThreading concepts. I came across ReentrantLock. It has methods lock and trylock. As I studied them further, I understood that they can be used instead of synchronized keyword block or method. So I tried them again with classic example of AccountDanger given in Kathie Sierra book. I observed that lock method DO allow turn taking with other threads. But trylock boolean method NOT allow turn taking with other threads. example below:

with lock method

package p1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AccountDanger implements Runnable {
    private Account account  = new Account();
    private int amt = 10;
    Lock lock = new ReentrantLock();
    Object obj = new Object();
    public void run(){

        for(int i=0;i<10;i++){
            lock.lock();
            try{
                if(account.getBalance()>=amt){
                    System.out.println(Thread.currentThread().getName()+" is going to withdraw..");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    account.withdraw(amt);
                    System.out.println(Thread.currentThread().getName()+" has withdrawn. Balance left is : "+account.getBalance());
                }
                else{
                    System.out.println("not enough balance for "+Thread.currentThread().getName());
                }
            }finally{
                lock.unlock();
            }
        }
        if(account.getBalance()<0){
            System.out.println("account is over withdrawn!!!");
        }
    }

    public static void main(String[] args) throws InterruptedException{
        AccountDanger ad = new AccountDanger();
        Thread t1 = new Thread(ad,"Mark");
        Thread t2 = new Thread(ad,"Phew");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("final balance left is : "+ad.account.getBalance());
    }
}

above code with lock method allowing both Mark and Phew threads to take turns.

with trylock method

package p1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AccountDanger implements Runnable {
    private Account account  = new Account();
    private int amt = 10;
    Lock lock = new ReentrantLock();
    Object obj = new Object();
    public void run(){

        for(int i=0;i<10;i++){
            try{
            if(lock.tryLock()){
            if(account.getBalance()>=amt){
                System.out.println(Thread.currentThread().getName()+" is going to withdraw..");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                account.withdraw(amt);
                System.out.println(Thread.currentThread().getName()+" has withdrawn. Balance left is : "+account.getBalance());
            }
            else{
                System.out.println("not enough balance for "+Thread.currentThread().getName());
            }
            }
            }
            finally {
                if(lock.tryLock()){
                    lock.unlock();
                }
            }
            if(account.getBalance()<0){
                System.out.println("account is over withdrawn!!!");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException{
        AccountDanger ad = new AccountDanger();
        Thread t1 = new Thread(ad,"Mark");
        Thread t2 = new Thread(ad,"Phew");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("final balance left is : "+ad.account.getBalance());
    }
}

above code does NOT allowing Phew thread to take turns even when Thread.sleep() method is called.

Account class

package p1;

public class Account{
    private int balance = 100;

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }


    public void withdraw(int amount){
        this.balance = this.balance - amount;
    }
}

although both methods are, no doubt, preventing balance to get negative. But I don't understand that why is trylock being so serious about the process that it does not allow other thread to come in between the execution even when Thread.sleep is called within. Even if I use lock.unlock statement in finally block after catch block of Thread.sleep, then also only mark thread is executing.

JPG
  • 1,247
  • 5
  • 31
  • 64
  • 1
    "I don't understand that why is trylock being so serious about the process that it does not allow other thread to come in between the execution even when Thread.sleep is called within" you mean, why does the obtained `Lock` prevent other threads from entering? That's its whole point. – daniu Jan 11 '18 at 08:26
  • ok, does that mean that if trylock has achieved a lock it would not release it unless unlock is called in finally – JPG Jan 11 '18 at 08:29
  • Yes, `trylock` is equivalent to `lock` except that it doesn't block (ie it returns immediately if the lock cannot be obtained). – daniu Jan 11 '18 at 08:32
  • however if I add finally block adjacent to thread.sleeps catch block with unlock method in it, then also `phew` thread not enter in there – JPG Jan 11 '18 at 08:32
  • 1
    That's because the thread passed the `trylock` without obtaining the lock, and since it won't try again it won't notice that it's released eventually. – daniu Jan 11 '18 at 08:36
  • ok, do you mean, that if `phew` thread returned out having false for `trylock`, it won't come again.. If that is so, then I understood. please confirm. – JPG Jan 11 '18 at 08:43
  • Well it does come again (10 times due to the loop), but since it can't obtain the lock every time, the loop will have finished and the *phew` thread terminated before the other thread's `sleep`. – daniu Jan 11 '18 at 08:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/162961/discussion-between-jsk-and-daniu). – JPG Jan 11 '18 at 08:52
  • @JSK in you finally block you used if (lock.tryLock()) that if succesfull increment the lock count for your thread, therefore if the first lock.tryLock() was succesfull, you'll never unlock it. – ugo Jan 11 '18 at 08:52

1 Answers1

1

Your basic misunderstanding was that trylock did not aquire a lock, which it does; as I stated in my comment, trylock is equivalent to lock except it doesn't block if the lock cannot be obtained.

What's happening in your case is this:

  1. the Mark thread is started and obtains the lock, then goes to sleep for 0.,5 seconds.
  2. in this time, the Phew thread is started. It tries to obtain the lock but fails because Mark has it.
  3. the next loop iteration is started for Phew; Phew tries to obtain the lock again and fails. This happens the 10 times the loop is run.
  4. the loop finishes; this exits the thread's run method and terminated the Phew thread.
  5. the Mark thread's sleep finishes.
  6. the finally block releases the lock.
  7. the next loop iteration is started for Mark. It tries to obtain the lock and succeeds (there's no one else around to obtain the lock anymore). Again, this happens a total of 10 times.
daniu
  • 14,137
  • 4
  • 32
  • 53
  • now understood completely with above steps you explained. Actually I also forgot that lock never released even when sleep is called. Thank you sir, you worked hard to explain me. Multithreading is always so complex. Always need a guru like you for resolving that complex thread structure.. – JPG Jan 11 '18 at 10:54