Let\'s say I have a shared object with field data
. Multiple threads will share a reference to this object in order to access the field. The threads never access
Regarding the second part of your question: if you do not use volatile on your variable X, it is possible that a given thread will always use a locally cached version of the value of the variable. Your use of variable Y as a lock will work very well as a means to insure that the two threads do not write concurrently to X but can't guarantee that one of the threads won't be looking at stale data.
From the JLS: "A write to a volatile variable v synchronizes-with all subsequent reads of v by any thread". The way I read this is that the spec offers no guarantees about the reads to other variables besides v.
You've only told part of the story with the counter. The incrementing part of the counter seems fine -- as Marko points out, there is a HB edge at Thread.start. But who's reading this counter? If it's anybody other than these spawned threads, and you at all care about seeing an up-to-date value, then the field needs to be volatile. If the counter is a long
(or double
), you need it to be volatile even if you don't care about stale values, because otherwise you could get word tearing.
Studying the entire JLS chapter on the Java Memory Model is highly recommended – mandatory, in fact – for anyone doing concurrency in Java. Your case, specifically, is covered in JLS, 17.4.4:
"An action that starts a thread synchronizes-with the first action in the thread it starts."
This means that for your first scenario you don't need volatile
. However, it would be good practice to have it anyway to be robust to future changes to the code. You should really have a good reason not to have volatile
, which would be only in the case of an incredibly high read rate (millions per second at the very least).
the Java Memory Model and bytecode reordering does not guarantee that subsequent thread will see the incremented value of the counter. So if you work with single thread - you don't need to do anything with volatiles, but if several threads may read something from variable - you need to ensure visibility of changes to another threads either with volatile or with syncrhonization/locks.
Thread.start method imposes the barrier, so visibility is assured - and it may happen that you don't need that volatile stuff. But I would add it anyway.
Mutations from a thread are guaranteed to become visible to other threads only when an happen-before relationship between the threads is established. When the relationship is established, all previous mutations become visible.
An object that isn't correctly synchronized when taken in isolation can be safe to use if another object correctly synchronizes accesses to it (see piggibacking in Java Concurrency in Practice).
In the two cases described in the question, I think no synchronization is needed:
Thread.start
establishes a happen-before relationship, so all mutations from previous threads are visibleIf you know that an object X is never accessed concurrently, chances are there is an object Y that indirectly synchronizes accesses to X, so it's fine. The only unsafe case I see is if threads relay on time itself (e.g. with Thread.sleep or by looping until some time has elasped) to guarantee mutual exclusion: in this case there is no happen-before relationship that is established.