Preferring synchronized to volatile
Asked Answered
C

4

11

I've read this answer in the end of which the following's written:

Anything that you can with volatile can be done with synchronized, but not vice versa.

It's not clear. JLS 8.3.1.4 defines volatile fields as follows:

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).

So, the volatile fields are about memory visibility. Also, as far as I got from the answer I cited, reading and writing to volatile fields are synched.

Synchronization, in turn guarantees that the only one thread has access to a synched block. As I got, it has nothing to do with memory visibility. What did I miss?

Crosspollination answered 28/9, 2015 at 8:6 Comment(1)
Entering and exiting a synchronized block performs a memory barrier, ensuring that changes are visible across threads.Matey
B
7

In fact synchronization is also related to memory visibilty as the JVM adds a memory barrier in the exit of the synchronized block. This ensures that the results of writes by the thread in the synchronization block are guaranteed to be visible by reads by another threads once the first thread has exited the synchronized block.

Note : following @PaŭloEbermann's comment, if the other thread go through a read memory barrier (by getting in a synchronized block for example), their local cache will not be invalidated and therefore they might read an old value.

The exit of a synchronized block is a happens-before in this doc : http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

Look for these extracts:

The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.

and

An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.

Beast answered 28/9, 2015 at 8:28 Comment(5)
Excellent answer, thank you very much. Didn't think that under the memory visilibility concept there was more general concept.Crosspollination
I would understand this slightly differently: changes in (and actually also before) the synchronization block are visible only to other threads which afterwards do synchronize on the same object (or have some other way of being in a "happens-after" relation), not to all reads by all other threads.Holily
@PaŭloEbermann indeed, if other threads do not synchronize (or provoque in any way a read memory barrier), their local cache will not be invalidated and therefore they might read an old value.Elfriedeelfstan
@PaŭloEbermann : not really sure how to write it down clearly and precisely. Please Edit if you have a better explaination on this issueElfriedeelfstan
@St.Antario, When you read this answer, remember that "happens-before" has special meaning in the Java Language Spec (JLS). When the spec says "A happens before B" it means A is guaranteed to happen before B. So, when they say, "X is true only if the write operation happens-before the read", they don't mean that X will be true if the write actually happens before the read: They mean that X is guaranteed to be true only if the write is guaranteed to happen before the read. You can use synchronized blocks to guarantee that a write in one thread happens before a read in another.Wagtail
P
2

Synchronized and volatile are different, but usually both of them are used to solve same common problem.

Synchronized is to make sure that only one thread will access the shared resource at a given point of time.

Whereas, those shared resources are often declared as volatile, it is because, if a thread has changed the shared resource value, it has to updated in the other thread also. But without volatile, the Runtime, just optimizes the code, by reading the value from the cache. So what volatile does is, whenever any thread access volatile, it wont read the value from the cache, instead it actually gets it from the actual memory and the same is used.


Was going through log4j code and this is what I found.

/**
 * Config should be consistent across threads.
 */
protected volatile PrivateConfig config;
Poly answered 28/9, 2015 at 8:29 Comment(1)
Then why use "volatile' keyword at all? You can gain multi thread access without specifying volatile or synchronized.Cupcake
T
1

If multiple threads write to a shared volatile variable and they also need to use a previous value of it, it can create a race condition. So at this point you need use synchronization.

... if two threads are both reading and writing to a shared variable, then using the volatile keyword for that is not enough. You need to use a synchronized in that case to guarantee that the reading and writing of the variable is atomic. Reading or writing a volatile variable does not block threads reading or writing. For this to happen you must use the synchronized keyword around critical sections.

For detailed tutorial about volatile, see 'volatile' is not always enough.

Trillbee answered 28/9, 2015 at 8:30 Comment(1)
For simple cases like a thread-safe counter or cas, using an Atomic type is usually easier and performs better than a synchronized block, especially at high contention.Nova
B
0

That's wrong. Synchronization has to do with memory visibility. Every thread has is own cache. If you got a lock the cache is refresehd. If you release a lock the cache is flused to the main memory.

If you read a volatile field there is also a refresh, if you write a volatile field there is a flush.

Beckford answered 28/9, 2015 at 8:23 Comment(1)
Caches are always coherent. And there is no flushing to main memory.Rosabella

© 2022 - 2024 — McMap. All rights reserved.