Reputation: 251
My program has two Threads, each prints ten numbers. The first Thread prints the odd number, the second Thread prints the even number, and they take turns printing numbers. I'm expecting to get a sequence like 1,2,3,4,5....until 20, but the program produces an IllegalMonitorStateException.
I know what this Exception means, but I use wait()
and notify()
in the synchronized block. Here is my code:
public class EvenOddThreadTest {
/**
* @param args
*/
static Object obj1 = new Object();
static Object obj2 = new Object();
static Object obj3=new EvenOddThreadTest();
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
for (int i = 1; i < 21; i += 2) {
synchronized (obj1) {
System.out.println(i + Thread.currentThread().getName());
try {
obj2.notify();
obj1.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 2; i < 21; i += 2) {
synchronized (obj2) {
System.out.println(i + Thread.currentThread().getName());
try {
obj1.notify();
obj2.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
}
}
and this is the Exception generated:
1Thread-0
2Thread-1
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at EvenOddThreadTest$1.run(EvenOddThreadTest.java:21)
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at EvenOddThreadTest$2.run(EvenOddThreadTest.java:41)
I can't figure this out. Any ideas?
Upvotes: 0
Views: 16471
Reputation: 2656
Both threads need to synchronize on the same variable, if they are working together. What you want is for one to wait for its turn to run, then run, then notify the other thread.
public class EvenOddThreadTest {
/**
* @param args
*/
static Object obj1 = new Object();
static Object obj3=new EvenOddThreadTest();
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
for (int i = 1; i < 21; i += 2) {
synchronized (obj1) {
try {
while (/* not my turn to run *?*/) {
obj1.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + Thread.currentThread().getName());
obj1.notify();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 2; i < 21; i += 2) {
synchronized (obj1) {
try {
while (/* not my turn to run *?*/) {
obj1.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i + Thread.currentThread().getName());
obj1.notify();
}
}
}
}.start();
}
}
Upvotes: 4
Reputation: 328608
This does not answer your question directly but Threads are quite low level. In your case, you could for example use a CyclicBarrier, which will handle the synchronization details for you:
public class Test {
static CyclicBarrier barrier = new CyclicBarrier(2);
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
try {
for (int i = 1; i < 21; i += 2) {
System.out.println(i + Thread.currentThread().getName());
barrier.await();
}
} catch (BrokenBarrierException e) {
//do something
} catch (InterruptedException e) {
//do something
}
}
}.start();
new Thread() {
@Override
public void run() {
try {
for (int i = 2; i < 21; i += 2) {
barrier.await();
System.out.println(i + Thread.currentThread().getName());
}
} catch (BrokenBarrierException e) {
//do something
} catch (InterruptedException e) {
//do something
}
}
}.start();
}
}
Upvotes: 0
Reputation: 3254
In thread 0 you are only synchronizing on the obj2
not obj1
on which you invoke notify
. In the second thread it's the other way around.
Upvotes: 1
Reputation: 32407
You can't call notify on an object whose monitor you don't own:
synchronized (obj1) {
System.out.println(i + Thread.currentThread().getName());
try {
obj2.notify(); // You haven't synchronized on obj2
Upvotes: 7