Readers and Writers problem

Hi,

I just thought of a case to illustrate the use of the "synchronized" keyword. As per my understanding, if a method is scoped as synchronized, then only one thread will be executing on it at any instant of time.

In this example, I have a "Bound" object which maintains two integers - lower and upper. The constraint is that at any point of time, lower <= upper. Let me assume that the domain is {0,1,2,3,4,5}.

class Bound

{

privateint lower, upper;

public Bound(int lower,int upper)

{

setLower(lower);

setUpper(upper);

}

publicint getLower()

{

return lower;

}

publicint getUpper()

{

return upper;

}

publicsynchronizedvoid setUpper(int value)

{

if (value < lower)

{

thrownew IllegalArgumentException();

}

upper = value;

}

publicsynchronizedvoid setLower(int value)

{

if (value > upper)

{

thrownew IllegalArgumentException();

}

lower = value;

}

}

Now I have two thread classes - a writer and a reader. The writer sets random values onto the "Bound" object, and the reader keeps reading those values:

class Writerextends Thread

{

private Bound b;

private Random r;

public Writer(Bound b)

{

this.b = b;

r =new Random();

setDaemon(true);

}

publicvoid run()

{

while(true)

{

try

{

b.setLower(Math.abs(r.nextInt()%6));

b.setUpper(Math.abs(r.nextInt()%6));

}

catch(IllegalArgumentException e)

{

//Ignore the exception

}

}

}

}

class Readerextends Thread

{

private Bound b;

public Reader(Bound b)

{

this.b = b;

}

publicvoid run()

{

long currTime = System.currentTimeMillis();

while(true)

{

int lower = b.getLower();

int upper = b.getUpper();

System.out.println(" " + lower +" " + upper);

if (upper < lower)

{

long endTime = System.currentTimeMillis();

System.out.println("Inconsistency occurred in " + (endTime - currTime) +" ms");

break;

}

}

}

}

Now I create a Bound object, and share it with three writers and a reader:

publicclass ThreadTest

{

publicstaticvoid main(String[] args)

{

Bound b =new Bound(0,5);

Thread t1 =new Writer(b);

Thread t2 =new Writer(b);

Thread t3 =new Writer(b);

Thread t4 =new Reader(b);

t1.start();

t2.start();

t3.start();

t4.start();

try

{

t4.join();

}

catch (InterruptedException e)

{

e.printStackTrace();

}

}

}

I never expected the inconsistency to occur because the setXXX( ) methods were synchronized. But however, in all the trial runs, I have ended up in an inconsistent Bound object, within 30 seconds or so.

I am sure that there must be something wrong in my code :-). Could anyone of you point out the mistake?

Thanks and Regards,

Kumar.

[6667 byte] By [kumar_iyera] at [2007-11-27 10:00:17]
# 1

No two threads will be able to use any one of those methods at the same time, however, one can call setLower at the same time another is calling setUpper, and nothing stops the read methods from being called at the same time as any of the write methods.

I think you need to learn a little more about synchronization.;-)

masijade.a at 2007-7-13 0:31:35 > top of Java-index,Java Essentials,Java Programming...
# 2

> No two threads will be able to use any one of

> those methods at the same time

> however, one can call setLower at the same time another is calling

> setUpper

No. How? They are both synchronized on the same object, i.e. 'this'.

> and nothing stops the read methods from

> being called at the same time as any of the write

> methods.

Now this is correct. I would synchronize the get methods and see if the problem still occurs. I think you have a staleness problem.

> I think you need to learn a little more about

> synchronization.;-)

Hmm ...

ejpa at 2007-7-13 0:31:35 > top of Java-index,Java Essentials,Java Programming...
# 3

> however, one can call

> setLower at the same time another is calling

> setUpper,

Masijade,

Never! setLower(..) and setUpper(..) are mutually exclusive, because they are implicitly synchronized on the "this" object.

EJP,

Yes, you could synchronize the readXXX(..) methods too. But that might not solve the problem, will it. What do you think about the scenario given below (assume R is the reader, and Wn is a writer):

Present state of Bound object = (3,4)

Rcalls getLower( ) which gives 3

W1 calls setLower(1)

W2 calls setUpper(2)

Rcalls getUpper( )which gives 2

Hence R will print "3,2" and hence an inconsistency; though the Bound object *actually* has the values (1,2). This problem will be aggravated if you put the Reader thread to sleep, between its 2 successive getLower( ) and getUpper( ) calls.

So that would mean that we must do a single read operation, and synchronize it too. Probably:

public synchronized int[] getLowerAndUpper()

{

return new int[] {lower, upper};

}

will solve the problem.

Do you know any other way of doing this?

Thanks and Regards,

Kumar.

kumar_iyera at 2007-7-13 0:31:35 > top of Java-index,Java Essentials,Java Programming...
# 4
I agree, returning a tuple is the best way.
ejpa at 2007-7-13 0:31:35 > top of Java-index,Java Essentials,Java Programming...
# 5

> > No two threads will be able to use any one

> of

> > those methods at the same time

> > however, one can call setLower at the same time

> another is calling

> > setUpper

>

> No. How? They are both synchronized on the same

> object, i.e. 'this'.

>

Because I can be an idiot at times (most maybe ;-) ) and type before I think. Oh, well, back to the drawing board.

> > and nothing stops the read methods from

> > being called at the same time as any of the write

> > methods.

>

> Now this is correct. I would synchronize the get

> methods and see if the problem still occurs. I think

> you have a staleness problem.

>

> I think you need to learn a little more about

> synchronization.;-)

>

> Hmm ...

Probably true in any case. ;-)

masijade.a at 2007-7-13 0:31:35 > top of Java-index,Java Essentials,Java Programming...