From: Lew on
Lew wrote:
>>>> String is the most basic immutable class in the API.

Tom Anderson wrote:
>>> Object?

Lew wrote:
>> You're tardy to the party on that one. Asked and answered and driven
>> to the depths of absurdity upthread already.

Tom Anderson wrote:
> Again, again!

Okay.

The upshot was:

Eric Sosman wrote:
>> (Prediction: Lew's answer will be "Yes," accompanied by a treatise
>> on the philosophic phylogeny of para-abstract psycho-paradigms, which
>> no one will be able to follow -- not even Lew!)

Lew wrote:
> It depends on the ontological semantics of "basic". Beginning Java
> programmers typically are introduced to the use of 'String' well
> before 'Integer' so in a pedagogical sense 'String' is the more basic
> type. Of course, within the para-abstract psycho-paradigm of inherent
> basicity, no one could argue for 'String' over 'Integer'.

--
Lew
From: Jim Janney on
Andreas Leitgeb <avl(a)gamma.logic.tuwien.ac.at> writes:

> Jim Janney <jjanney(a)shell.xmission.com> wrote:
>> Peter Duniho <NpOeStPeAdM(a)NnOwSlPiAnMk.com> writes:
>>> I mean sure, there are ways that it could have been written
>>> incorrectly. The main (only?) non-thread-safe mistake being to use
>>> the "hash" field itself as the accumulator for the calculation.
>>
>> public class SannyCode {
>> [ uses the "hash" field itself as the accumulator for the calculation.]
>> }
>
> That's the one sin which Pete already mentioned.

OK, here's a version that doesn't do that.

public class SannyCode {
private final String s;
private boolean calculated = false;
private long code = 0;

public SannyCode(String s) {
this.s = s;
}

public long getCode() {
if (!calculated) {
calculated = true;
long c = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) != '-') {
c += i;
}
}
code = c;
}
return code;
}
}

Still not thread-safe. In any case, we seem to be in agreement:
carelessly (as opposed to carefully) written code can be immutable but
not thread-safe.

--
Jim Janney
From: Andreas Leitgeb on
Jim Janney <jjanney(a)shell.xmission.com> wrote:
> Andreas Leitgeb <avl(a)gamma.logic.tuwien.ac.at> writes:
>> Jim Janney <jjanney(a)shell.xmission.com> wrote:
>>> Peter Duniho <NpOeStPeAdM(a)NnOwSlPiAnMk.com> writes:
>>>> I mean sure, there are ways that it could have been written
>>>> incorrectly. The main (only?) non-thread-safe mistake being to use
>>>> the "hash" field itself as the accumulator for the calculation.
>>> public class SannyCode {
>>> [ uses the "hash" field itself as the accumulator for the calculation.]
>>> }
>> That's the one sin which Pete already mentioned.
> OK, here's a version that doesn't do that.
> public class SannyCode {
> private final String s;
> private boolean calculated = false;
> private long code = 0;
>
> public SannyCode(String s) {
> this.s = s;
> }
>
> public long getCode() {
> if (!calculated) {
> calculated = true;
> long c = 0;
> for (int i = s.length() - 1; i >= 0; i--) {
> if (s.charAt(i) != '-') {
> c += i;
> }
> }
> code = c;
> }
> return code;
> }
> }
> Still not thread-safe.

Actually, I do think, this *is* thread-safe.
You have to care for being more careless than that :-D

> In any case, we seem to be in agreement:
> carelessly (as opposed to carefully) written code can be
> immutable but not thread-safe.

And it's likely, that if one is *so* careless as to really make his
immutable class thread-unsafe, that he will accidentally make it
inadvertedly mutable as well ;-)

From: Andreas Leitgeb on
Andreas Leitgeb <avl(a)gamma.logic.tuwien.ac.at> wrote:
> Jim Janney <jjanney(a)shell.xmission.com> wrote:
>> Andreas Leitgeb <avl(a)gamma.logic.tuwien.ac.at> writes:
>>> Jim Janney <jjanney(a)shell.xmission.com> wrote:
>>>> Peter Duniho <NpOeStPeAdM(a)NnOwSlPiAnMk.com> writes:
>>>>> I mean sure, there are ways that it could have been written
>>>>> incorrectly. The main (only?) non-thread-safe mistake being to use
>>>>> the "hash" field itself as the accumulator for the calculation.
>>>> public class SannyCode {
>>>> [ uses the "hash" field itself as the accumulator for the calculation.]
>>>> }
>>> That's the one sin which Pete already mentioned.
>> OK, here's a version that doesn't do that.
>> public class SannyCode {
>> private final String s;
>> private boolean calculated = false;
>> private long code = 0;
>>
>> public SannyCode(String s) {
>> this.s = s;
>> }
>>
>> public long getCode() {
>> if (!calculated) {
>> calculated = true;
>> long c = 0;
>> for (int i = s.length() - 1; i >= 0; i--) {
>> if (s.charAt(i) != '-') {
>> c += i;
>> }
>> }
>> code = c;
>> }
>> return code;
>> }
>> }
>> Still not thread-safe.
>
> Actually, I do think, this *is* thread-safe.

I retract that. It *is* indeed thread unsafe.
A second thread might reach the "if" exactly just after the first
thread set calculated to true, and return 0 for the code. I "hate"
myself, for missing that before ;)

From: Tom Anderson on
On Sun, 18 Jul 2010, Andreas Leitgeb wrote:

> Jim Janney <jjanney(a)shell.xmission.com> wrote:
>> Andreas Leitgeb <avl(a)gamma.logic.tuwien.ac.at> writes:
>>> Jim Janney <jjanney(a)shell.xmission.com> wrote:
>>>> Peter Duniho <NpOeStPeAdM(a)NnOwSlPiAnMk.com> writes:
>>>>> I mean sure, there are ways that it could have been written
>>>>> incorrectly. The main (only?) non-thread-safe mistake being to use
>>>>> the "hash" field itself as the accumulator for the calculation.
>>>> public class SannyCode {
>>>> [ uses the "hash" field itself as the accumulator for the calculation.]
>>>> }
>>> That's the one sin which Pete already mentioned.
>> OK, here's a version that doesn't do that.
>> public class SannyCode {
>> private final String s;
>> private boolean calculated = false;
>> private long code = 0;
>>
>> public SannyCode(String s) {
>> this.s = s;
>> }
>>
>> public long getCode() {
>> if (!calculated) {
>> calculated = true;
>> long c = 0;
>> for (int i = s.length() - 1; i >= 0; i--) {
>> if (s.charAt(i) != '-') {
>> c += i;
>> }
>> }
>> code = c;
>> }
>> return code;
>> }
>> }
>> Still not thread-safe.
>
> Actually, I do think, this *is* thread-safe.

Nope. Start with an instance where calculated = false and code = 0. Thread
A turns up and starts executing getCode. It gets as far as 'long c = 0',
at which point it ends its timeslice. For some reason, its writes are
flushed to memory. calculated is now true, and code is still 0. Thread B
turns up and starts executing getCode. It sees calculated is true, so
skips over the if block, and returns the currents contents of code, which
is 0. Failure has occurred.

In fact, it's worse than that. Thread A could finish the method and update
both calculated and code, but because there is no happens-before
relationship between thread A and thread B, it's possible that B could
come along later, and see the updated calculated but *not* the updated
code. So even without an unlucky timeslice end, there is no guarantee of
safety here.

To make this method safe, you either have to synchronize the whole thing,
or do the update of calculated and code atomically at the end. You could
do the updates together in a synchronized block, but i think you might
then also need to put any reads of them in synchronized blocks too. The
alternative would be a volatile or atomic variable that holds an object
containing both calculated and code, i think.

The beauty of the String approach, of using a special value of code to
indicate that it had not been calculated, is that you don't need any
synchronisation for safety, just the JLS's guarantee of no word tearing in
writes and reads of int variables. Because it combines the flag and value
fields in a single int, they are read and written atomically as a pair. Of
course, were you to do this, you might want to avoid String's ability to
generate a code which looks like a flag indicating the lack of a code (ie
0). But then, you might think the one in four billion chance of it
happening was insignificant.

tom

--
Suddenly, everything is clear ...