Not understanding wait & notify

This test app keeps crashing and I am unsure why

publicclass ThreadTest{

public Integer counter;

/**

* @param args

*/

publicstaticvoid main(String[] args){

// TODO Auto-generated method stub

ThreadTest t =new ThreadTest();

t.run();

}

publicvoid run(){

counter =new Integer(0);

Thread workerThread;

for (int y = 0; y < 10; y++){

workerThread =new Thread(){

publicvoid run(){

try{

Thread.sleep(1000);

}catch (InterruptedException e){

// TODO Auto-generated catch block

e.printStackTrace();

}

incThreads();

try{

Thread.sleep(2000);

}catch (InterruptedException e){

// TODO Auto-generated catch block

e.printStackTrace();

}

decThreads();

}

};

workerThread.start();

}

waitOnthreads();

}

publicvoid incThreads(){

synchronized (counter){

System.out.println("inc in");

counter++;

System.out.println("inc out");

}

}

/**

* Decrement thread count

*/

publicvoid decThreads(){

synchronized (counter){

System.out.println("dec in");

counter--;

if (counter == 0){

counter.notify();

}

System.out.println("dec out");

}

}

publicvoid waitOnthreads(){

// Wait for the whole window to build

// Including the sub frames

synchronized (counter){

System.out.println("wait int");

if (counter != 0){

try{

counter.wait();

}catch (InterruptedException e1){

e1.printStackTrace();

System.exit(0);

}

}

}

System.out.println("wait out");

}

}

[4255 byte] By [KeyzerSuzea] at [2007-11-27 5:13:10]
# 1
Hi i remember reading that the synchronized object should not be modified , ie like in this case counter is modified in the synchronized block synchronized on counter only . Try synchronizing with a single object not onto itself
bhvijaya at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...
# 2

But when I change the synchronized to around the methods instead of code blocks it works ?

public class ThreadTest {

public Integer counter;

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

ThreadTest t = new ThreadTest();

t.run();

}

public void run() {

counter = new Integer(0);

Thread workerThread;

for (int y = 0; y < 10; y++) {

workerThread = new Thread() {

public void run() {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

incThreads();

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

decThreads();

}

};

workerThread.start();

}

waitOnthreads();

}

public synchronized void incThreads() {

System.out.println("inc in");

counter++;

System.out.println("inc out");

}

/**

* Decrement thread count

*/

public synchronized void decThreads() {

System.out.println("dec in");

counter--;

if (counter == 0) {

notify();

}

System.out.println("dec out");

}

public synchronized void waitOnthreads() {

// Wait for the whole window to build

// Including the sub frames

System.out.println("wait int");

if (counter != 0) {

try {

wait();

} catch (InterruptedException e1) {

e1.printStackTrace();

System.exit(0);

}

}

System.out.println("wait out");

}

}

KeyzerSuzea at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...
# 3

but this code will lead to race condition .

so what you have to do is create a object for example

Object obj = new Object();

As you synchronized on counter earlier . synchronize on this object and do wait and notify on this object.

ie your code will look like

synchronized (obj ) {

... .. . .

}

and obj.wait() or obj.notify()

hope this helps

Vijay

bhvijaya at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...
# 4
okay so what your saying then is if I take a monitor/lock on an object and it changes that breaks the monitor/lock. So when i increment/dec it it changes
KeyzerSuzea at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...
# 5

> Hi

> i remember reading that the synchronized object

> should not be modified , ie like in this case counter

> is modified in the synchronized block synchronized on

> counter only .

Not exactly, but the problem is related to that. It comes from that innocent looking statements like

counter++;

Note that counter is an Integer and the compiler is using autoboxing when compiling this line. The long version the compiler is generating is something like

int tmp = counter.intValue();

tmp++;

counter = new Integer(tmp);

You see, the variable counter is referring to a different object after that statement. An Integer is immutable, so for incrementing a new Integer must be created and the old one (which hold the lock) is discarede. The sync is therefore not using a single object, but another one after each inc/dec operation. This is certainly not what you want.

So using some other object for syncing or just "this" (by moving the synchronized keyword to the method declaration) seems to be a good idea.

Message was edited by:

martin@work

martin@worka at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...
# 6
Great thanks for that, I had a bit of a sleep on it and I was coming to this conclusion as well.
KeyzerSuzea at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...
# 7
You should use an object exclusively for locking always. Don't overwork the object by giving it multiple roles.
_dnoyeBa at 2007-7-12 10:34:36 > top of Java-index,Core,Core APIs...