JPG
JPG

Reputation: 1253

ReentrantLock's trylock method not allowing turn taking of multiple threads

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.

Upvotes: 0

Views: 1024

Answers (1)

daniu
daniu

Reputation: 14999

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.

Upvotes: 1

Related Questions