Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / December 2005

Tip: Looking for answers? Try searching our database.

Thread Safe

Thread view: 
ndxp@hotmail.com - 14 Dec 2005 11:12 GMT
class safe {

public static int foo;

public static void setfoo (int x) { foo = x; }

public static void getfoo() { return foo; }

}

I read somewhere, for java the methods setfoo and getfoo are atomic.
Does
that mean, that I don't have to declared the methods synchronized ?

-t
J. Verdrengh - 14 Dec 2005 11:51 GMT
Apparently not

http://www.javaworld.com/javaworld/javaqa/2003-02/01-qa-0214-threadsafe.html
Benji - 14 Dec 2005 17:42 GMT
> http://www.javaworld.com/javaworld/javaqa/2003-02/01-qa-0214-threadsafe.html 

The author does not explain the problem with the following code:
    Thread1            Thread2
       ...                  MaybeSafe.setFoo(1);
       MaybeSafe.setFoo(2); ...
       ...                  MaybeSafe.getFoo();

If there's no relationship between the two calls to setFoo, then you
shouldn't be able to have a guarentee about which one's value will
propagate to getFoo.  The only way that this can affect the correctness of
the program is if there is some dependant relationship between setFoo(1) and
setFoo(2) - however, if there is a dependant relationship between them, it
would be non-atomic, and would require a synchronized block, which would
create a write barrier, and flush the values, and so our "problem" goes
away.

He also criticizes java's implementation of get and set properties, but
I'm not sure the problem exists there.

I feel like maybe I'm missing something important, since he seems to have
far more experience than I do - can anyone explain to me a sitaution in
which his example could actually create a problem?

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Jaakko Kangasharju - 15 Dec 2005 07:52 GMT
>> http://www.javaworld.com/javaworld/javaqa/2003-02/01-qa-0214-threadsafe.html 
>
[quoted text clipped - 7 lines]
> shouldn't be able to have a guarentee about which one's value will
> propagate to getFoo.

That's exactly the problem that the author describes.  Without
synchronization in Thread1 and Thread2 you are not guaranteed that the
effect of setFoo in Thread1 is ever seen by Thread2.

> The only way that this can affect the correctness of the program is
> if there is some dependant relationship between setFoo(1) and
> setFoo(2) - however, if there is a dependant relationship between
> them, it would be non-atomic, and would require a synchronized
> block, which would create a write barrier, and flush the values, and
> so our "problem" goes away.

You're right, it's not really a safety issue, but more of a liveness
issue (safety = nothing undesirable happens, liveness = something
desirable happens).  Presumably if you call a set method you want the
new value to be seen by the whole program, so you need to synchronize
(or if you are just getting and setting a single primitive value, make
it volatile).

> He also criticizes java's implementation of get and set properties, but
> I'm not sure the problem exists there.

It is the same thing.  You are not guaranteed that the properties set
by one thread with setProperties are ever seen by other threads
because there is no synchronization.

Signature

Jaakko Kangasharju, Helsinki Institute for Information Technology
Every analogy is faulty in exactly the places where
the presenter's argument is weakest

Benji - 15 Dec 2005 08:07 GMT
> It is the same thing.  You are not guaranteed that the properties set
> by one thread with setProperties are ever seen by other threads
> because there is no synchronization.

I think I agree with you as far as what you're saying happens...

however, you're also not guarenteed that the setting thread won't get
context switched out right before it sets the properties - and then the
system will go for even longer before getting the properties set.  So
I think the problem that you're describing (which really isn't a problem
so much as a small delay, and is unavoidable anyway due to nondeterminism
of context switching) is different from the problem that the author is
describing, which is that the code is "not thread safe" (his words).

If he thinks that that code is not thread safe, then he can't think that
any code that doesn't use a real time scheduler is thread safe.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Jaakko Kangasharju - 15 Dec 2005 08:38 GMT
>> It is the same thing.  You are not guaranteed that the properties set
>> by one thread with setProperties are ever seen by other threads
[quoted text clipped - 3 lines]
> context switched out right before it sets the properties - and then the
> system will go for even longer before getting the properties set.

Of course, you cannot rely on any execution order (unless you
implement your own deterministic scheduling).  But if the scheduler
does not have liveness problems (i.e., if it eventually schedules any
ready thread), then you're guaranteed that your setting runs at some
point.  And when it runs, you need the synchronization to propagate
the effects to other threads.

> So I think the problem that you're describing (which really isn't a
> problem so much as a small delay, and is unavoidable anyway due to
> nondeterminism of context switching) is different from the problem
> that the author is describing, which is that the code is "not thread
> safe" (his words).

I don't think it is different.  To me it seems that the author is
merely misusing the term "thread safe" to cover both safety and
liveness issues.  At least that's what I get from his explanation.

Signature

Jaakko Kangasharju, Helsinki Institute for Information Technology
It is the programmer's responsibility to write clean code
It is the compiler's responsibility to generate efficient code

Benji - 15 Dec 2005 08:51 GMT
> implement your own deterministic scheduling).  But if the scheduler
> does not have liveness problems (i.e., if it eventually schedules any
> ready thread), then you're guaranteed that your setting runs at some
> point.  And when it runs, you need the synchronization to propagate
> the effects to other threads.

a context switch is the same as a synchronize - the cache gets flushed and
all writes are propagated to main memory (which in turn sets the dirty bit
on the caches for other threads, and forces them to reload from main
memory the next time they access the variable).

So, I still don't think that synchronized fixes anything.  (it may make it
slightly quicker, but the variable will be synchronized whenever the
thread switches for sure - which means that you aren't buying any better
guarentees for when the value will be propagated)

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Jaakko Kangasharju - 15 Dec 2005 10:28 GMT
> a context switch is the same as a synchronize - the cache gets
> flushed and all writes are propagated to main memory (which in turn
> sets the dirty bit on the caches for other threads, and forces them
> to reload from main memory the next time they access the variable).

Okay, this was the issue.  Java's memory model does not guarantee this
(at least according to Doug Lea's book).  Only synchronized and
volatile guarantee that changes made by one thread are visible to
other threads, thread context switches do not do that.

Signature

Jaakko Kangasharju, Helsinki Institute for Information Technology
Do you cry alone, do you cough by yourself?

Roedy Green - 15 Dec 2005 10:35 GMT
On Thu, 15 Dec 2005 12:28:53 +0200, Jaakko Kangasharju
<jkangash@hiit.fi> wrote, quoted or indirectly quoted someone who said

>> a context switch is the same as a synchronize - the cache gets
>> flushed and all writes are propagated to main memory (which in turn
>> sets the dirty bit on the caches for other threads, and forces them
>> to reload from main memory the next time they access the variable)

 A context switch does not flush registers to their corresponding
variables. If other threads look at those variables, they will be out
of date, unless special measures were taken.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Steve Horsley - 16 Dec 2005 01:15 GMT
> On Thu, 15 Dec 2005 12:28:53 +0200, Jaakko Kangasharju
> <jkangash@hiit.fi> wrote, quoted or indirectly quoted someone who said
[quoted text clipped - 7 lines]
> variables. If other threads look at those variables, they will be out
> of date, unless special measures were taken.

Right.

In theory, thread-1 could call setFoo(1) on Wednesday, thread-2
could call setFoo(2) on Thursday, and thread-1 could call
getFoo() on Friday and find that it's still 1. This is definitely
an issue of timeliness.

Steve
Chris Uppal - 14 Dec 2005 12:27 GMT
> I read somewhere, for java the methods setfoo and getfoo are atomic.

They are atomic, yes.

> Does
> that mean, that I don't have to declared the methods synchronized ?

No, it does not mean that.  You have to use a synchronised block (or declare
the relevant data volatile) or else changes made by one thread are not
guaranteed to be seen by any other thread.  "Atomic" only means that no thread
will see /part/ of the assignment (e.g. the high 16-bits set to the new value,
but the low 16-bits still with the old value).

   -- chris
ndxp@hotmail.com - 14 Dec 2005 13:26 GMT
chris,

can throw more light on this ?

Atomic" only means that no thread
> will see /part/ of the assignment (e.g. the high 16-bits set to the new value,
> but the low 16-bits still with the old value).

thanks

> > I read somewhere, for java the methods setfoo and getfoo are atomic.
>
[quoted text clipped - 10 lines]
>
>     -- chris
Benji - 14 Dec 2005 22:52 GMT
> can throw more light on this ?

What chris was talking about could be better explained by this:  let's say
you have a 'field' that is actually two fields, and you have to set the
two fields together.  So let's say this is your class:

public class Modulus
{
    private int numerator;
    private int denominator;
    public setValue(int numerator, int denominator)
    {
        this.numerator = numerator;
        //thread context switch could happen here
        this.denominator = denominator;
    }
    public double getValue()
    {
        return ((double)numerator)/denominator;
    }
}

in the example, you could call setValue, and if the thread switched out at
the specified point, the object could be left in an inconsistant state.
(the numerator would be set, but the denominator would not - in other words,
it would have a value that you never specified)

this can actually happen if you use a long or a double - assignment happens
in multiple steps that can have a thread switch happen between the steps.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Alun Harford - 15 Dec 2005 02:00 GMT
>> can throw more light on this ?
>
[quoted text clipped - 27 lines]
> happens
> in multiple steps that can have a thread switch happen between the steps.

Not it can't. Get an introduction to bytecode, it's very useful stuff.

Alun Harford
Roedy Green - 15 Dec 2005 03:56 GMT
On Thu, 15 Dec 2005 02:00:52 +0000 (UTC), "Alun Harford"
<alunharford@yahoo.com> wrote, quoted or indirectly quoted someone who
said :

>Not it can't. Get an introduction to bytecode, it's very useful stuff.

In an interpreted machine with green threads that would be so, but not
after the code is converted to machine code with OS threads as it is
in hotspot.. You can then get a context switch at any time.  Task are
completely unaware of when they happen so they can't very well patch
things to avoid them except at byte code instruction boundaries..
Tasks have full amnesia of the time they were out unless they check
the CPU cycle clock.

Unless Java is doing something very clever, threads are left in an
inconsistent state when other threads take over.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

ndxp@hotmail.com - 15 Dec 2005 05:47 GMT
I'm still confused ! I don't have much experience in thread programing.
Best way, I guess would be to write a "proof of concept" program which
should indicate that this little piece of code is thread safe or not. I
would like to run this on all JVMs.

> On Thu, 15 Dec 2005 02:00:52 +0000 (UTC), "Alun Harford"
> <alunharford@yahoo.com> wrote, quoted or indirectly quoted someone who
[quoted text clipped - 15 lines]
> Canadian Mind Products, Roedy Green.
> http://mindprod.com Java custom programming, consulting and coaching.
Benji - 15 Dec 2005 08:01 GMT
> I'm still confused ! I don't have much experience in thread programing.
> Best way, I guess would be to write a "proof of concept" program which
> should indicate that this little piece of code is thread safe or not. I
> would like to run this on all JVMs.

Actually, that's probably not a good way of testing it.  Especially since
you probably have a single processor machine, in which case you're never
going to see this behavior.  Even if you have a dual processor machine
(hyperthreading does not count), a P4's memory architecture has stronger
guarentees than the java memory model, so you're still not going to see
all of the possible problems.  Not only that, but actually finding a
threading bug could take days of running the same code over and over again.

You're much better off understanding what's going on.

In your case, I seriously doubt that synchronizing will help anything with
accessors and modifiers like your original question asked.  However, if
you don't want to put the time into learning how threading stuff works, it
might be better just to slap synchronized around stuff when in doubt - it
wont' slow your program down that much unless it's something that's in a
really tight loop.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Roedy Green - 15 Dec 2005 10:28 GMT
>I'm still confused ! I don't have much experience in thread programing.
>Best way, I guess would be to write a "proof of concept" program which
>should indicate that this little piece of code is thread safe or not. I
>would like to run this on all JVMs.

It is very difficult to debug thread code. You have to become paranoid
and simply imagine all possible horrible things that could happen and
make sure you have them covered. You can never test enough to cover
all possibilities, especially all possibilities on different hardware,
platforms, oses.

The keys are:

1. keep the threads from meddling the same variables.

2. don't do raw thread/synchronized/wait/notify code.  Use classes
that encapsulate that logic. See the concurrent package e.g. Queue

http://mindprod.com/jgloss/queue.html

Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Benji - 15 Dec 2005 07:54 GMT
> Not it can't. Get an introduction to bytecode, it's very useful stuff.

You might not want to go around telling people to get introductions to
things that you don't understand yourself, luv.  :-*

Just because the bytecode operation is one step doesn't mean that it's
going to be translated into one step in the native code that is compiled
by the VM.

The Java Memory Model does not guarentee that double and long will be
written atomically.  See

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.7

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Alun Harford - 16 Dec 2005 17:30 GMT
>> Not it can't. Get an introduction to bytecode, it's very useful stuff.
>
[quoted text clipped - 9 lines]
>
> http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.7

Hmm... my apologies.
I look silly now.

Alun Harford
Chris Uppal - 15 Dec 2005 10:01 GMT
> can throw more light on this ?
>
> > Atomic" only means that no thread
> > will see /part/ of the assignment (e.g. the high 16-bits set to the new
> > value, but the low 16-bits still with the old value).

I'll try a different tack from that which Benji took.  Maybe this will be too
low-level to help you, but it's the best I can do.

For a starter, imagine that the Java program is running on an 8-bit machine --
that's to say that the path to main memory ("bus") is 8-bits wide.  (Such
machines may still exist, but you are unlikely to find yourself using one, this
is just the starting point for my example).  Since the memory bus can only
carry 8 bits at a time, it will take 4 operations (at least) to write one
32-bit int to main store.  So if you call:
   setFoo(0x12345678);
then the underlying hardware will execute 4 separate updates of main memory.
The exact order is not important, but say it writes out:
       0x12
       0x34
       0x56
       0x78
to four consecutive 8-bit locations in main memory.  That would effectively
copy the (32-bit) integer value into 32 bits of main memory.  Now imagine that
a different thread on the same machine were attempting to perform:
   setFoo(0xABABABAB);
If that happened at the same time as the other call to setFoo(), then -- unless
someone did something special to prevent it -- the resulting four writes of
0xAB could happen /arbitrarily/ interleaved with those of 0x12345667.  So in
the end the main memory might hold:
   0x 12 AB AB 78
The result is that from the point of view of the program, although we had only
ever called setFoo() with 0x12345678 and 0xABABABAB, a completely different
value could have ended up in the "foo" variable.

Things would be worse if foo was an object reference instead of a 32-bit int.
In that case, not only could we end up with a value of "foo" that had never
been supplied by setFoo(), but that value could actually be garbage.  And if
that happened then the very /best/ result you could hope for is that your
program would crash immediately.

Now, this example has depended so far on the Java program running on ancient
hardware (or very constrained hardware).  On more modern machines the bus is
32-bits wide, but the same sort of problem is still possible.  That would
happen if the memory used to hold "foo" were not aligned on a 32-bit boundary
(very unlikely), or if the memory used to hold "x" (the parameter to setFoo())
was not held on a 32-bit boundary (possible).  In such cases, the single 32-bit
value "x" cannot be transferred to main memory in a single 32-bit write.  In
practise, on any /specific/ machine, there is a way to ensure that such
problems do not actually occur.  And that is the point of the "atomic"
guarantee in the Java language definition.  JVM implementations are /required/
to take whatever steps are necessary to ensure that such problems do not occur.

In a later post in this thread, you asked:

> I'm still confused ! I don't have much experience in thread programing.
> Best way, I guess would be to write a "proof of concept" program which
> should indicate that this little piece of code is thread safe or not. I
> would like to run this on all JVMs.

At least as far as this "atomic" issue is concerned, nobody can give you a code
snippet that shows the problem -- that's because no legal JVM is allowed to
/have/ a problem ;-)

On the other hand, as I mentioned, the "atomic" property (although very
valuable for correct programs) isn't itself enough -- not even nearly -- to be
able to write thread-safe programs.  See my reply to Benji in this thread (a
few) for more details.

   -- chris
Roedy Green - 15 Dec 2005 10:39 GMT
On Thu, 15 Dec 2005 10:01:13 -0000, "Chris Uppal"
<chris.uppal@metagnostic.REMOVE-THIS.org> wrote, quoted or indirectly
quoted someone who said :

> And that is the point of the "atomic"
>guarantee in the Java language definition.

are non-volatile long writes guaranteed atomic?

IIRC that is not so.

On a Pentium, arranging for longs to be stored atomically with native
threads could be expensive.  I'm not sure how you would go about it.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Steve Horsley - 16 Dec 2005 01:29 GMT
> On Thu, 15 Dec 2005 10:01:13 -0000, "Chris Uppal"
> <chris.uppal@metagnostic.REMOVE-THIS.org> wrote, quoted or indirectly
[quoted text clipped - 9 lines]
> On a Pentium, arranging for longs to be stored atomically with native
> threads could be expensive.  I'm not sure how you would go about it.

It is my understanding that writes (and reads) of long values are
not guaranteed to be atomic, although all other primitives are
(including object references, of course). I can't find where I
read that now.

Steve
Thomas Hawtin - 16 Dec 2005 01:53 GMT
>> are non-volatile long writes guaranteed atomic?
>>
[quoted text clipped - 6 lines]
> guaranteed to be atomic, although all other primitives are (including
> object references, of course). I can't find where I read that now.

long and double reads and writes are not guaranteed to be atomic,
*except* if they are volatile. However, Sun's JVM for x86 has a bug in
that respect. I'm not entirely sure why they don't just make it slow,
like trig functions.

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Roedy Green - 16 Dec 2005 04:14 GMT
On Fri, 16 Dec 2005 01:59:38 +0000, Thomas Hawtin
<usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone
who said :

>long and double reads and writes are not guaranteed to be atomic,
>*except* if they are volatile. However, Sun's JVM for x86 has a bug in
>that respect. I'm not entirely sure why they don't just make it slow,
>like trig functions.

What I'm curious about is what sort of MASM code does Sun generate to
insure a save is  atomic if a long is declared volatile? What tools
are available to snoop on Sun's dynamically generated machine code?

Is it as primitive as acquiring a lock that all threads use for all
64-bit saves?  Please no!

There are possible problems with 32 bit quantities too. For anyone
unfamiliar with word tearing, here's a good explanation taken from
David R. Butenhof's book, Programming with POSIX Threads.

If a variable crosses the boundary between memory units, which can
happen if the machine supports unaligned memory access, the computer
may have to send the data in two bus transactions. An unaligned 32-bit
value, for example, may be sent by writing the two adjacent 32-bit
memory units. If either memory unit involved in the transaction is
simultaneously written from another processor, half of the value may
be lost. This is called "word tearing."
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Thomas Hawtin - 16 Dec 2005 17:52 GMT
> On Fri, 16 Dec 2005 01:59:38 +0000, Thomas Hawtin
> <usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone
[quoted text clipped - 7 lines]
> What I'm curious about is what sort of MASM code does Sun generate to
> insure a save is  atomic if a long is declared volatile?

It doesn't for x86. That is the bug.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4023233

>                                                          What tools
> are available to snoop on Sun's dynamically generated machine code?

IIRC, there is code to dump (dis)assembler produced by HotSpot, but
licensing restrictions prevent it being generally available. You
favourite debugger should be able to manage. I just tried gdb, but
couldn't get it to do anything but whinge.

> Is it as primitive as acquiring a lock that all threads use for all
> 64-bit saves?  Please no!

The slightly more efficient solution is to take a value based upon the
hash code of the containing object. Use the bottom few bits as an index
into a block of spin locks.

> There are possible problems with 32 bit quantities too. For anyone
> unfamiliar with word tearing, here's a good explanation taken from
> David R. Butenhof's book, Programming with POSIX Threads.

Sun's JVM always aligns objects to eight byte boundaries.

Tom Hawtin
Signature

Unemployed English Java programmer
http://jroller.com/page/tackline/

Roedy Green - 17 Dec 2005 07:20 GMT
On Fri, 16 Dec 2005 17:57:53 +0000, Thomas Hawtin
<usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone
who said :

>> There are possible problems with 32 bit quantities too. For anyone
>> unfamiliar with word tearing, here's a good explanation taken from
>> David R. Butenhof's book, Programming with POSIX Threads.
>
>Sun's JVM always aligns objects to eight byte boundaries.

but within an object what happens with alignment?  Does Sun move all
the doubles to the front of the object and 8-byte align them?  From
what I have been reading about tearing with Intel, they had better
align fields.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Roedy Green - 17 Dec 2005 07:24 GMT
On Fri, 16 Dec 2005 17:57:53 +0000, Thomas Hawtin
<usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone
who said :

>> There are possible problems with 32 bit quantities too. For anyone
>> unfamiliar with word tearing, here's a good explanation taken from
>> David R. Butenhof's book, Programming with POSIX Threads.
>
>Sun's JVM always aligns objects to eight byte boundaries.

I wonder if there is some way to statically analyse code or
dynamically monitor code running interpretively to detect potential
trouble spots where two different threads look at or write to
variables outside sync blocks.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Roedy Green - 16 Dec 2005 04:18 GMT
On Fri, 16 Dec 2005 01:59:38 +0000, Thomas Hawtin
<usenet@tackline.plus.com> wrote, quoted or indirectly quoted someone
who said :

>long and double reads and writes are not guaranteed to be atomic,
>*except* if they are volatile. However, Sun's JVM for x86 has a bug in
>that respect. I'm not entirely sure why they don't just make it slow,
>like trig functions.

From the  IA-32 Intel Architecture Software Developer's Manual Volume
3 : System Programming Guide

7.1.1 Guaranteed Atomic Operations

The Pentium 4, Intel Xeon, P6 family, Pentium, and Intel486 processors
guarantee that the following basic memory operations will always be
carried out atomically:

. Reading or writing a byte

. Reading or writing a word aligned on a 16-bit boundary

. Reading or writing a doubleword aligned on a 32-bit boundary

The Pentium 4, Intel Xeon, and P6 family, and Pentium processors
guarantee that the following additional memory operations will always
be carried out atomically:

. Reading or writing a quadword aligned on a 64-bit boundary.

. 16-bit accesses to uncached memory locations that fit within a
32-bit data bus.

The P6 family processors guarantee that the following additional
memory operations will always be carried out atomically:

. Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit
within a 32-byte cache line.

Accesses to cacheable memory that are split across bus widths, cache
lines and page boundaries are not guaranteed to be atomic by the
Pentium 4, Intel Xeon, P6 family, Pentium and Intel486 processors. The
Pentium 4, Intel Xeon and P6 family processors provide bus control
signals that permit external memory subsystems to make split accesses
atomic; however, nonaligned data accesses will seriously impact the
performance of the processor and should be avoided.
Signature

Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.

Benji - 14 Dec 2005 17:32 GMT
> class safe {

> public static int foo;

> public static void setfoo (int x) { foo = x; }

> public static void getfoo() { return foo; }

> }

> I read somewhere, for java the methods setfoo and getfoo are atomic.
> Does
> that mean, that I don't have to declared the methods synchronized ?

the methods are atomic in the sense that the only operation they do
(reading and writing to a field) is guarenteed to be carried out
uninturrupted.

However, "thread safe" is tricky.  What a later poster is referring to is
the fact that foo can be set by one thread on one processor, and the value
will not propagate immediately to the other processor.

however, I disagree with the other poster in that I think this is as thread
safe as you can possibly get it - because there is no case in which you
would need the value of foo to be immediately propagated across threads
in which you were not using a synchronized block, which would propagate the
values.  I would be very interested if someone could come up with an
example where synchronized would help anything as far as correctness of a
program goes.

"correct values" are a tricky subject with threads, because it's all a
relative thing.  usually you go for "the most correct value", with the
caveat that "I won't require any better precision".  (similar to distributed
time protocols)

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

Chris Uppal - 15 Dec 2005 09:44 GMT
> however, I disagree with the other poster in that I think this is as
> thread safe as you can possibly get it - because there is no case in
[quoted text clipped - 4 lines]
> example where synchronized would help anything as far as correctness of a
> program goes.

If by "correct" you mean that the program does what the author intended, and
what the reader would (presumably) expect, then how about this:

class Worker
implements Runnable
{
   boolean m_stopRequested;

   public void
   run()
   {
       while (shouldKeepGoing())
           oneStepOfWork();
   }

   public void
   requestStop()
   {
       m_stopRequested = true;
   }

   boolean
   shouldKeepGoing()
   {
       return m_stopRequested;
   }

   private void
   oneStepOfWork()
   {
       //...
   }
}

That defines a simple worker object that can be started as a separate thread,
and will continue to run until someone asks it to stop.  That can malfunction.
It is pretty close to thread-safe -- the assignment to m_stopRequested, and the
reads of the same variable, are atomic, so there is no question of garbage
appearing in that variable.  What's more the variable is a latch (is only ever
changed once) so there is no question of race conditions.  However, without
without synchronisation in requestStop() and shouldKeepGoing(), there is no
guarantee that changes to m_stopRequested that are made in one thread will
/ever/ be visible to the thread where the worker is running.  That counts as
incorrect execution in my book.

Just for completeness, this problem could also be fixed by declaring
m_stopRequested to be "volatile".  That might not be a bad option in this very
particular example, but in real world code using explicit synchonisation is
usually the only simple and/or sufficient option.

   -- chris
Chris Uppal - 15 Dec 2005 10:16 GMT
I wrote:

> without without synchronisation in
> requestStop() and shouldKeepGoing(), there is no guarantee that changes
> to m_stopRequested that are made in one thread will /ever/ be visible to
> the thread where the worker is running.  That counts as incorrect
> execution in my book.

Having read you later posts in this thread, I think I should expand this a
little.  The point is /not/ a liveness issue, it's not that the change might
not be propogated across within an appropriate timeframe (for the specific
application).  The point is that, wthout synchronisation, there is nothing to
tell the compiler/JITer that it is not allowed to re-write a loop like:

    run()
    {
        while (! m_stopRequested)
            oneStepOfWork();
    }

into:

    run()
    {
       bolean aBool = m_stopRequested.
        while (! aBool)
            oneStepOfWork();
    }

and that's a correctness issue.  The same observation applies at hardware
level, although I don't know if there are any JVM implementation that run on
hardware (or even if there is any hardware) which defaults to such weak
propogation guarantees.

   -- chris
Benji - 15 Dec 2005 16:49 GMT
>      run()
>      {
>          while (! m_stopRequested)
>              oneStepOfWork();
>      }

> into:

>      run()
>      {
>         bolean aBool = m_stopRequested.
>          while (! aBool)
>              oneStepOfWork();
>      }

> and that's a correctness issue.  The same observation applies at hardware
> level, although I don't know if there are any JVM implementation that run on
> hardware (or even if there is any hardware) which defaults to such weak
> propogation guarantees.

of course, a loop like that isn't guarenteed not to starve all other processes
in the system.  =)

in practice, that's bad programming, and if you actually knew that a spinlock
would be a good solution, you would also be smart enough to know that you
should put a memory barrier inside of the while loop.

However, that's interesting.  I didn't think that was a possibility.  I was
under the impression that the JVM was not allowed to rewrite field accesses
like that - only local variables.  (I thought that fields automatically
had some properties of 'volatile' in that sense)  Maybe that's the .NET
framework?  Can you find a reference for this behavior?  I'm looking in the
JLS and I can't find anything that mentions it.  Just the absence of any
mention of it.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

John C. Bollinger - 16 Dec 2005 03:45 GMT
>>     run()
>>     {
[quoted text clipped - 10 lines]
>>             oneStepOfWork();
>>     }

[...]

> in practice, that's bad programming, and if you actually knew that a spinlock
> would be a good solution, you would also be smart enough to know that you
> should put a memory barrier inside of the while loop.

You can't have your cake and eat it too.  Your position a few posts ago
seems to have been that it should not be necessary to synchronize
exclusively for the purpose of ensuring that updates are propagated
across threads:

"however, I disagree with the other poster in that I think [atomic reads
and writes] is as thread safe as you can possibly get it - because there
is no case in which you would need the value of foo to be immediately
propagated across threads in which you were not using a synchronized
block, which would propagate the values.  I would be very interested if
someone could come up with an example where synchronized would help
anything as far as correctness of a program goes."

Chris responded with just the kind of example you requested, and now
you've effectively said "well, duh, you need to synchronize."  If you
have a point left anywhere in there then I'm missing it.

> However, that's interesting.  I didn't think that was a possibility.  I was
> under the impression that the JVM was not allowed to rewrite field accesses
[quoted text clipped - 3 lines]
> JLS and I can't find anything that mentions it.  Just the absence of any
> mention of it.

You might want to consider reading chapter 17 of JLS3, especially
sections 17.3 and 17.4.  These are all about the things that a
conforming Java program may do with shared variables.  The material
probably does not answer your question directly, but it certainly does
support Chris' assertion that a program could behave /as if/ it
performed the optimization he described.

Signature

John Bollinger
jobollin@indiana.edu

Benji - 15 Dec 2005 16:36 GMT
> reads of the same variable, are atomic, so there is no question of garbage
> appearing in that variable.  What's more the variable is a latch (is only ever
[quoted text clipped - 3 lines]
> /ever/ be visible to the thread where the worker is running.  That counts as
> incorrect execution in my book.

> Just for completeness, this problem could also be fixed by declaring
> m_stopRequested to be "volatile".  That might not be a bad option in this very
> particular example, but in real world code using explicit synchonisation is
> usually the only simple and/or sufficient option.

well, 2 things:
1) this is bad code.  =)  If you're explicitly waiting on the value of
a variable to be changed, you should use wait/notify, which uses
synchronized, which sould sync the variable.
2) even if you did want to spin-lock, you still have no guarentee that
the thread that sets the variable will ever get run.  the OS scheduler
has the option of freezing this thread indefinitely, just like you have
no guarentee that the thread's memory will get synchronized.  However,
in practice neither of these are concerns.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.

John C. Bollinger - 16 Dec 2005 03:04 GMT
> 1) this is bad code.  =)  If you're explicitly waiting on the value of
> a variable to be changed, you should use wait/notify, which uses
> synchronized, which sould sync the variable.

Well, the code isn't guaranteed to work correctly, which was exactly
Chris' point.  So yes, it's bad code in that sense.  On the other hand,
wait()/notify() is not correct here, because the thread checking for an
update specifically does *not* want to block on the change -- quite the
opposite, it wants to keep going until the change signals it to stop.
This is pretty much the standard idiom for gracefully stopping a running
thread, except that to make it correct you need to synchronize the write
and read (on the same monitor) to ensure that any update is seen by the
reading thread.  This, again, is the point: the atomicity of the read
and write are nowhere close to enough to provide thread safety.

> 2) even if you did want to spin-lock, you still have no guarentee that
> the thread that sets the variable will ever get run.  the OS scheduler
> has the option of freezing this thread indefinitely, just like you have
> no guarentee that the thread's memory will get synchronized.  However,
> in practice neither of these are concerns.

The thread-scheduling issue is less of a concern, I agree, but I think
you are discounting the update visibility much too much.

Signature

John Bollinger
jobollin@indiana.edu

Benji - 16 Dec 2005 04:57 GMT
> Well, the code isn't guaranteed to work correctly, which was exactly
> Chris' point.  So yes, it's bad code in that sense.  On the other hand,
> wait()/notify() is not correct here, because the thread checking for an
> update specifically does *not* want to block on the change -- quite the
> opposite, it wants to keep going until the change signals it to stop.

I think that my point was (which I now realize is incorrect) that if you
need to wait on something, the only good way to do it is with a synchronize
and a .wait().  However, now I realize that there are several correct
situations that could be optimized into incorrect code since the JVM is not
aware that a certain bit of code needs to be left as-is.

I think I was perhaps thrown off by the article that made it sound as though
the main problem was delay, which is a bit misleading - but I suppose it
depends on the application.

Thanks for your responses.

Signature

Of making better designs there is no end,
 and much refactoring wearies the body.



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.