IllegalMonitorStateException:
The most important thing in multi-threading is serializing the simultaneous access to the objects, otherwise these multiple threads will leave this object i
n an unstable state.For this purpose locks have been introduced.
The synchronized keyword is the easiest one for providing synchronization between the threads and it has an intrinsic lock associated with it.When a method is declared to be synchronized then only one thread can gain access to the method at a time.The next thread will get a chance only after the previous thread exits the method.
When a thread gains access but has no sufficient sources to accomplish its tasks then it has to give chance to other threads.For this purpose
wait(),notify(),notifyAll() methods have been introduced.
Note: In Thread class there is no methods like wait() or notify() is available.It is only available in Object class.So any objects can call this method.Calling this method outside the synchronized block will also throw this exception.
In java API documentation it is given that calling these methods will throw java.lang. IllegalMonitorStateException when the current thread is not the owner of the object's lock.
Let us see in detail,when this illegalMonitorStateException is thrown.
There are two possible ways,a synchronized keyword can be used to acquire the lock.
1.A method can be declared as synchronized.
2.Using a synchronized block to acquire a lock on an specific object.
When a method is declared as synchronized,then all the threads are synchronized by the
class object that contains this method i.e,All the threads can have locks over the class object.
I have explained this concept with a bank containing three accounts.we have to trasfer funds from
one account to other accounts.This transfering process is carried out by each threads.
The following two programs are written by cay s.Horstmann in (Core Java,VolumeI),
Copyright© 2008 Sun Microsystems,Inc. I have modified this program so as to make it simple to explain this exception.
Program:
public class UnsynchBank
{
public static void main(String[] args)throws InterruptedException
{
Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE);
int i;
TransferRunnable r = new TransferRunnable(b, 0,1,500,INITIAL_BALANCE);
t = new Thread(r);
t.start();
TransferRunnable r2 = new TransferRunnable(b, 0,2,500,INITIAL_BALANCE);
t1=new Thread(r2);
t1.start();
}
private static Thread t,t1;
public static final int NACCOUNTS = 3;
public static final double INITIAL_BALANCE = 1000;
}
class Bank
{
public Bank(int n, double initialBalance)throws InterruptedException
{
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
}
public synchronized void transfer(int from, int to, double amount) throws InterruptedException
{
while(accounts[from]
{
System.out.println("i am entering into wait set:"+Thread.currentThread());
wait();//
This wait() method is called by the thread that has acquired lock on the class object Bank
}
accounts[from] -= amount;
accounts[to] += amount;
System.out.println("I am the current thread:"+Thread.currentThread());
System.out.println("source:"+from);
System.out.println("to:"+to);
System.out.println("amount:"+amount);
System.out.printf("Total Balance: %10.2f%n", getTotalBalance());
System.out.println("currentthread"+Thread.currentThread());
notify();//This notify() method is called by the thread that has acquired a lock on the class object
Bank
System.out.println();
}
public synchronized double getTotalBalance()
{
double sum = 0;
for (double a : accounts)
sum += a;
return sum;
}
private final double[] accounts;
}
class TransferRunnable implements Runnable
{
public TransferRunnable(Bank b, int from,int to,int a, double max)
{
bank = b;
fromAccount = from;
toaccount=to;
amt=a;
maxAmount = max;
}
public void run()
{
try
{
int toAccount = toaccount;
int amount = amt;
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int) (DELAY * Math.random()));
}
catch (InterruptedException e)
{
}
}
private Bank bank;
private int fromAccount;
private double maxAmount;
private int toaccount;
private int amt;
private int DELAY = 10;
}
Output:
I am the current thread:Thread[Thread-1,5,main]
source:0
to:2
amount:500.0
Total Balance: 3000.00
currentthreadThread[Thread-1,5,main]
I am the current thread:Thread[Thread-0,5,main]
source:0
to:1
amount:500.0
Total Balance: 3000.00
currentthreadThread[Thread-0,5,main]
In the above program all the threads are synchronized by the
Bank class object.I have shown this by highlighting those statements in the program. If you try to call wait() or notify() on any
other objects IllegalMonitorStateException will be thrown. For eg, if you try to call accounts.wait(); this will throw illegalmonitorstateexception because all threads have acquired the lock on the class object Bank not on the accounts object.
Using synchronized blocks:
Program:
In the above program change the transfer method as follows to use synchronized blocks to acquire locks.
public void transfer(int from, int to, double amount) throws InterruptedException
{
synchronized(accounts) //All the threads are synchronized on the accounts object
{
while(accounts[from]
{
System.out.println("i am entering into wait set:"+Thread.currentThread());
accounts.wait();//wait() method is called by the thread that has a lock on the
accounts object
}
accounts[from] -= amount;
accounts[to] += amount;
System.out.println("I am the current thread:"+Thread.currentThread());
System.out.println("source:"+from);
System.out.println("to:"+to);
System.out.println("amount:"+amount);
System.out.printf("Total Balance: %10.2f%n", getTotalBalance());
System.out.println("currentthread"+Thread.currentThread());
accounts.notify();//notify
() method is called by the thread that has a lock on the accounts object.
System.out.println();
}
}
output:
I am the current thread:Thread[Thread-0,5,main]
source:0
to:1
amount:500.0
Total Balance: 3000.00
currentthreadThread[Thread-0,5,main]
Bank@1b67f74
Exception in thread "Thread-0" I am the current thread:Thread[Thread-1,5,main]ja
va.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
source:0 at Bank.transfer(UnsynchBank.java:70)
at TransferRunnable.run(UnsynchBank.java:129)
to:2 at java.lang.Thread.run(Unknown Source)
amount:500.0
Total Balance: 3000.00
currentthreadThread[Thread-1,5,main]
Bank@1b67f74
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at Bank.transfer(UnsynchBank.java:70)
at TransferRunnable.run(UnsynchBank.java:129)
at java.lang.Thread.run(Unknown Source)
If you see the program,all the threads are synchronized on the
accounts object.So when calling the wait() or notify() method all the thread should specify the object on which it has acquired the lock,
for eg, accounts.wait(). Simply calling the wait() method indicates that we are trying to call wait() on the bank object not on the accounts object.
So when we synchronize the threads using a paricular
object obj.We should call
obj.wait() or
obj.notify().