Hello,
By searching the web I found lots of references about the below
question. At the same time, I couldn't find an *authoritative*
references (eg. by Goetz, Lea or Bloch) about it. Please refer me to the
correct web site or book. Thanks (I'm using JVM 1.3, so the "old" memory
model)
The question: Are the following code snippets thread safe and why? (my
opinion is on top, please correct if wrong):
// Not thread safe. "value" needs to be volatile, else another thread
// might see a stale value
public final class Test1 {
private int value = 0;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
// Not thread safe. Because assignment to long is not atomic (although
// volatile).
public final class Test2 {
private volatile long value = 0;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
// Thread safe. Because reference assignment is atomic
// Question: Has immutability of String any influence here?
// Question: Could "newValue" be incompletely constructed?
public final class Test3 {
private volatile String value = "Hello";
public String getValue() {
return value;
}
public void setValue(String newValue) {
this.value = newValue;
}
}
// Not thread safe. Because construction may not have finished when
// reference assignment is made (although volatile).
public final class Test4 {
private volatile Date value = new Date();
public Date getValue() {
return value;
}
public void makeNewDate() {
this.value = new Date();
}
}
// (Still) not thread safe. Because 2 step assignement makes no
// guaranteeing that the ref will not be assigned earlier.
public final class Test5 {
private volatile Date value = new Date();
public Date getValue() {
return value;
}
public void makeNewDate() {
Date d = new Date();
this.value = d;
}
}
Thanks for your answers and clarifications.
Phil
|
|
0
|
|
|
|
Reply
|
sicsicsic (164)
|
4/30/2008 12:38:21 PM |
|
On Apr 30, 2:38=A0pm, Philipp <sicsic...@freesurf.ch> wrote:
> ...
> The question: Are the following code snippets thread safe and why? (my
> opinion is on top, please correct if wrong):
What is your definition of thread safeness in the case of your
examples?
> // Not thread safe. "value" needs to be volatile, else another thread
> // might see a stale value
> public final class Test1 {
> =A0 =A0 =A0private int value =3D 0;
>
> =A0 =A0 =A0public int getValue() {
> =A0 =A0 =A0 =A0 =A0return value;
> =A0 =A0 =A0}
> =A0 =A0 =A0public void setValue(int value) {
> =A0 =A0 =A0 =A0 =A0this.value =3D value;
> =A0 =A0 =A0}
> }
This construction is regarded not to be thread safe. However, with
volatile int, you only solve the so-called visibility problem but I
would not regard the whole class thread safe even with the volatile
int. Here the question again what do you regard to be a stale value.
> // Not thread safe. Because assignment to long is not atomic (although
> // volatile).
> public final class Test2 {
> =A0 =A0 =A0private volatile long value =3D 0;
>
> =A0 =A0 =A0public long getValue() {
> =A0 =A0 =A0 =A0 =A0return value;
> =A0 =A0 =A0}
> =A0 =A0 =A0public void setValue(long value) {
> =A0 =A0 =A0 =A0 =A0this.value =3D value;
> =A0 =A0 =A0}
> }
Atomicity and volatileness are two different issues: "Locking can
guarantee both visibility and atomicity; volatile variables can only
guarantee visibility." (Java Concurrency in Practice, By Brian Goetz,
Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea)
As far as I know, however, single read or single write is atomic in
case of volatile long, so it should be thread safe in your
interpretation of thread safeness.
I think the rest of the examples, Test3-5 are all unsafe because of
the same reason: Atomicity and volatileness are two different issues.
In those cases you require atomicity to make it thread safe.
Best Regards,
Szabolcs
|
|
0
|
|
|
|
Reply
|
szabolcs.ferenczi (129)
|
4/30/2008 3:56:42 PM
|
|
"Philipp" <sicsicsic@freesurf.ch> wrote in message
news:1209559089_4017@sicinfo3.epfl.ch...
> Hello,
> By searching the web I found lots of references about the below question.
> At the same time, I couldn't find an *authoritative* references (eg. by
> Goetz, Lea or Bloch) about it. Please refer me to the correct web site or
> book. Thanks (I'm using JVM 1.3, so the "old" memory model)
I find the best summary to be Doug Lea's "Concurrent Programming..." p.
92-97, which he says he takes from the JLS chapter 17 on the memory model.
My 2nd edition (C) 2000 copy says that this section is still being updated
for Java 2, so it is likely to be reasonably "authoritative" for 1.3.
However, he points out (and I have read from others) that the early memory
model is unclear in some places (p. 95 footnote) and downright broken in
others. And regardless of what is authoritatively written, any given JVM of
that vintage may or may not be implemented perfectly.
>
> The question: Are the following code snippets thread safe and why? (my
> opinion is on top, please correct if wrong):
>
> // Not thread safe. "value" needs to be volatile, else another thread
> // might see a stale value
> public final class Test1 {
> private int value = 0;
>
> public int getValue() {
> return value;
> }
> public void setValue(int value) {
> this.value = value;
> }
> }
According to Paul Hyde's Java Thread Programming p. 127, "Before Sun
included JIT with their VMs, volatile did not make a difference."
Nevertheless, if this code is accessed from threads that are not otherwise
synchronized, the lack of volatile may cause setValues in thread to not be
visible to another. The volatile keyword is sufficient to fix that because
the accesses and updates to the value are independent. If the code had
expressions such as value = value * 4 or value++, these must be sychronized
because they will appear as two or more independent operations. As long as
the access or updates are recognized to be totally independent, volatile is
acceptable.
>
>
> // Not thread safe. Because assignment to long is not atomic (although //
> volatile).
> public final class Test2 {
> private volatile long value = 0;
>
> public long getValue() {
> return value;
> }
> public void setValue(long value) {
> this.value = value;
> }
> }
Doug Lea also asserts on p.93 that "atomicity extends to volatilie long and
double" even though they are not atomic when not volatile.
The next two cases are similar. They are threadsafe only to the extent that
the object reference is being updated in a timely and atomic way. There is
no guarantee of timely update for any fields of the assigned String or Date
and only their initial value will be visible. I'm not sure what counts as
"initial value" in this case, being that String and Date both assign most of
their fields in the constructor. This may be the uncertainty in the JLS that
Doug Lea talks about on p. 95.
As a clearer example of this point, if the object used for setValue had
non-volatile fields that were not assigned in the constructor, there is
nothing that guarantees that the updated values of those fields will be
visible in a different thread.
Matt Humphrey http://www.iviz.com/
|
|
0
|
|
|
|
Reply
|
matth (145)
|
4/30/2008 4:52:34 PM
|
|
Matt Humphrey wrote:
>
> As a clearer example of this point, if the object used for setValue
> had non-volatile fields that were not assigned in the constructor,
> there is nothing that guarantees that the updated values of those
> fields will be visible in a different thread.
Unless, of coujrse, the object itself guarantees that by synchronizing
the getter(s) and setter(s).
|
|
0
|
|
|
|
Reply
|
mscottschilling (1976)
|
4/30/2008 8:30:00 PM
|
|
"Mike Schilling" <mscottschilling@hotmail.com> wrote in message
news:cF4Sj.457$J16.156@newssvr23.news.prodigy.net...
> Matt Humphrey wrote:
>>
>> As a clearer example of this point, if the object used for setValue
>> had non-volatile fields that were not assigned in the constructor,
>> there is nothing that guarantees that the updated values of those
>> fields will be visible in a different thread.
>
> Unless, of coujrse, the object itself guarantees that by synchronizing the
> getter(s) and setter(s).
I agree, but this raises to my mind a case whether synchronized is necessary
or not. The case I'm thinking of is like this:
class Factorization {
private int [] factors;
public Factorization (int value) {
factors = new int [20];
computeFactors (value);
}
public void computeFactors (int value) {
// Internal code that populates the array
}
public int getTotal () {
if (factors == null) return 0;
int total = 0;
for (int i =0; i < factors.length; ++i) {
total += factors[i];
}
return total;
}
class MathBox {
private volatile Factorization factorization;
public Factorization getF () { return factorization; }
public void setF (Factorization f) { factorization = f; }
}
static MathBox sharedMathBox = new MathBox ();
// Writer thread
for (int i = 0; i < 100; ++i) {
Factorization factorization = new Factorization (i);
// Alternate position for computeFactors ()
sharedMathBox.setF (factorization);
}
// Reader thread
for (int i = 0; i < 100; ++i) {
Factorization factorization = sharedMathBox.getF ();
System.out.println ("" + factorization.getTotal ());
}
Despite the contrivance of 2 totally unsynchronized threads, what is not
clear to me is what the read thread will see for the values for the
factorization. The volatile keyword ensures that the factorization
reference arrives safely, but it doesn't guarantee the fields. The fields
are assigned within the execution of the constructor, but I don't think
that's sufficient; I don't think it counts as the visibility of the initial
value.
Will the reader sometimes see a total of 0 because factors is null? Will it
sometimes see an inconsistent result because not all of the values have been
updated? Or will it always be up-to-date because it falls within the
natural ordering of the constructor?
It does seem certain that if computeFactors is moved out of the constructor
and into the loop at the marked position that the reader thread may
sometimes see inconsistent results. Synchronizing computeFactors would
ensure that the reader always sees the up-to-date values.
I tested volatile on a 2-processor hyperthreaded system a couple years ago
and I could not get non-volatile values to fail. I would like to know what
the correct model is for this case, but I would only trust an empirical test
that actually showed a failure. (The test might not fail because of CPU
caching, JIT performance, etc.)
Matt Humphrey http://www.iviz.com/
|
|
0
|
|
|
|
Reply
|
matth (145)
|
5/1/2008 1:57:42 AM
|
|
Perhaps I can clarify. Szabolics' analysis is correct, but I would
put it slightly differently. Its not so much a question of "what do
you mean by safe." Its a question of what you expect from this
class.
By making the internal field volatile, you have made your class data-
race free. This means that callers of get() will see the most recent
value passed by any thread to set(). However, you cannot use this
class to safely build a counter, for example; uses like
foo.set(foo.get() + 1)
are in danger of "lost updates" because there's no way to make the
set(get()+1) operation atomic with respect to other ops on Test2
object. That might be OK.
Another way to put this is that while this class is safe, it can be
used in a not-thread-safe manner, and that doesn't make the class not
safe, it makes the use not safe.
On Apr 30, 11:56=A0am, Szabolcs Ferenczi <szabolcs.feren...@gmail.com>
wrote:
> On Apr 30, 2:38=A0pm, Philipp <sicsic...@freesurf.ch> wrote:
>
> > ...
> > The question: Are the following code snippets thread safe and why? (my
> > opinion is on top, please correct if wrong):
>
> What is your definition of thread safeness in the case of your
> examples?
>
> > // Not thread safe. "value" needs to be volatile, else another thread
> > // might see a stale value
> > public final class Test1 {
> > =A0 =A0 =A0private int value =3D 0;
>
> > =A0 =A0 =A0public int getValue() {
> > =A0 =A0 =A0 =A0 =A0return value;
> > =A0 =A0 =A0}
> > =A0 =A0 =A0public void setValue(int value) {
> > =A0 =A0 =A0 =A0 =A0this.value =3D value;
> > =A0 =A0 =A0}
> > }
>
> This construction is regarded not to be thread safe. However, with
> volatile int, you only solve the so-called visibility problem but I
> would not regard the whole class thread safe even with the volatile
> int. Here the question again what do you regard to be a stale value.
>
> > // Not thread safe. Because assignment to long is not atomic (although
> > // volatile).
> > public final class Test2 {
> > =A0 =A0 =A0private volatile long value =3D 0;
>
> > =A0 =A0 =A0public long getValue() {
> > =A0 =A0 =A0 =A0 =A0return value;
> > =A0 =A0 =A0}
> > =A0 =A0 =A0public void setValue(long value) {
> > =A0 =A0 =A0 =A0 =A0this.value =3D value;
> > =A0 =A0 =A0}
> > }
>
> Atomicity and volatileness are two different issues: "Locking can
> guarantee both visibility and atomicity; volatile variables can only
> guarantee visibility." (Java Concurrency in Practice, ByBrian Goetz,
> Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea)
>
> As far as I know, however, single read or single write is atomic in
> case of volatile long, so it should be thread safe in your
> interpretation of thread safeness.
>
> I think the rest of the examples, Test3-5 are all unsafe because of
> the same reason: Atomicity and volatileness are two different issues.
> In those cases you require atomicity to make it thread safe.
|
|
0
|
|
|
|
Reply
|
brian8769 (6)
|
5/1/2008 6:11:21 PM
|
|
brian@briangoetz.com wrote:
> Perhaps I can clarify. Szabolics' analysis is correct, but I would
> put it slightly differently. Its not so much a question of "what do
> you mean by safe." Its a question of what you expect from this
> class.
>
> By making the internal field volatile, you have made your class data-
> race free. This means that callers of get() will see the most recent
> value passed by any thread to set(). However, you cannot use this
> class to safely build a counter, for example; uses like
>
> foo.set(foo.get() + 1)
>
> are in danger of "lost updates" because there's no way to make the
> set(get()+1) operation atomic with respect to other ops on Test2
> object. That might be OK.
If I understand this correctly, then synchronizing set and get would
guarantee the correct counter behavior by making set and get *mutually*
exclusive, which is not guaranteed by volatile.
But is there a difference between the above and this?
int newValue = foo.get() + 1;
foo.set(newValue);
Does the call in one single line guarantee that the lock is passed
directly from the get to the set? Else, another thread with another get
might interfer.
Can a counter only be implemented with a specific synchronized
"increment()" method?
In summary, how can you guys sleep at night without making *everything*
synchronized? (and at the same time, having nightmares about deadlocks I
guess)... ? :-)
Phil
PS: Thanks for the references to Matt Humphrey and Szabolcs
|
|
0
|
|
|
|
Reply
|
sicsicsic (164)
|
5/5/2008 9:03:37 AM
|
|
Philipp wrote:
> brian@briangoetz.com wrote:
>> Perhaps I can clarify. Szabolics' analysis is correct, but I would
>> put it slightly differently. Its not so much a question of "what do
>> you mean by safe." Its a question of what you expect from this
>> class.
>>
>> By making the internal field volatile, you have made your class data-
>> race free. This means that callers of get() will see the most recent
>> value passed by any thread to set(). However, you cannot use this
>> class to safely build a counter, for example; uses like
>>
>> foo.set(foo.get() + 1)
>>
>> are in danger of "lost updates" because there's no way to make the
>> set(get()+1) operation atomic with respect to other ops on Test2
>> object. That might be OK.
>
> If I understand this correctly, then synchronizing set and get would
> guarantee the correct counter behavior by making set and get *mutually*
> exclusive, which is not guaranteed by volatile.
> But is there a difference between the above and this?
>
> int newValue = foo.get() + 1;
> foo.set(newValue);
>
> Does the call in one single line guarantee that the lock is passed
> directly from the get to the set? Else, another thread with another get
> might interfer.
>
> Can a counter only be implemented with a specific synchronized
> "increment()" method?
In general yes, unless updates are only happening on a single thread.
Mark Thornton
|
|
0
|
|
|
|
Reply
|
mark.p.thornton (29)
|
5/5/2008 9:42:15 AM
|
|
Philipp wrote:
> brian@briangoetz.com wrote:
>> Perhaps I can clarify. Szabolics' analysis is correct, but I would
>> put it slightly differently. Its not so much a question of "what do
>> you mean by safe." Its a question of what you expect from this
>> class.
>>
>> By making the internal field volatile, you have made your class data-
>> race free. This means that callers of get() will see the most recent
>> value passed by any thread to set(). However, you cannot use this
>> class to safely build a counter, for example; uses like
>>
>> foo.set(foo.get() + 1)
>>
>> are in danger of "lost updates" because there's no way to make the
>> set(get()+1) operation atomic with respect to other ops on Test2
>> object. That might be OK.
>
> If I understand this correctly, then synchronizing set and get would
> guarantee the correct counter behavior by making set and get *mutually*
> exclusive, which is not guaranteed by volatile.
> But is there a difference between the above and this?
Actually, that isn't any different than volatile really.
You have to synchronize the entire operation.
Imagine two threads A and B, and foo's value is 10
A: foo.get() is called, returns 10
B: foo.get() is called, returns 10
A: adds one to result (11) and stores it in newValue.
B: adds one to result (11) and stores it in newValue.
A: foo.set(11) gets called
B: foo.set(11) gets called.
Whoops, we just lost a count! What you need to do to ensure your
counter works as expected is to synchronize the "increment" operation.
>
> int newValue = foo.get() + 1;
> foo.set(newValue);
>
> Does the call in one single line guarantee that the lock is passed
> directly from the get to the set? Else, another thread with another get
> might interfer.
Exactly! The lock isn't passed on.
>
> Can a counter only be implemented with a specific synchronized
> "increment()" method?
Man, I should have read you're entire post before replying, you've got
the right idea.
>
> In summary, how can you guys sleep at night without making *everything*
> synchronized? (and at the same time, having nightmares about deadlocks I
> guess)... ? :-)
I sleep fine by knowing what must be an atomic operation, and what must
be thread-safe, and what doesn't need to be one or the other. I tend to
prefer Thread Confinement, such as having a separate Context per thread,
so mutable objects aren't passed to other threads. That approach is
useful in servlets. Thread Confinement is also used in AWT/Swing on the
Event Dispatch Thread.
>
> Phil
> PS: Thanks for the references to Matt Humphrey and Szabolcs
I suggest reading the book Java Concurrency in Practice by Doug Lea. It
is an excellent reference on what, why, and how in handle multi-threaded
programming. Its not too thick, and one of the few reference books
worth ready cover to cover.
--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
|
|
0
|
|
|
|
Reply
|
newsgroup.spamfilter (920)
|
5/5/2008 2:51:09 PM
|
|
Philipp wrote:
|> Can a counter only be implemented with a specific synchronized
|> "increment()" method?
Use AtomicInteger
--
"I'm a doctor, not a mechanic." Dr Leonard McCoy <mccoy@ncc1701.starfleet.fed>
"I'm a mechanic, not a doctor." Volker Borchert <v_borchert@despammed.com>
|
|
0
|
|
|
|
Reply
|
v_borchert (106)
|
5/6/2008 4:07:34 AM
|
|
|
9 Replies
22 Views
(page loaded in 1.57 seconds)
|