I am not sure I use @volatile
correctly here. I have a buffer, like this:
final class BufD(val buf: Array[Double], @volatile var size: Int)
Which is sent between processes, whereby it might cross thread boundaries. The sender may update the size
field just before sending it out. Therefore I want to make sure that the receiver under no circumstances can see a stale size
value here. First question: Does @volatile
ensure this or is it redundant?
Now I am introducing a trait:
trait BufLike {
@volatile var size: Int
}
final class BufD(val buf: Array[Double], @volatile var size: Int) extends BufLike
This gives me a compiler warning:
Warning:(6, 4) no valid targets for annotation on method size - it is discarded unused. You may specify targets with meta-annotations, e.g. @(volatile @getter)
@volatile var size: Int ^
Second question: Should I remove the @volatile
here or change it in a different way?
@volatile Marks a field which can change its value outside the control of the program; this is equivalent to the volatile modifier in Java.
Using volatile is yet another way (like synchronized, atomic wrapper) of making class thread-safe. Thread-safe means that a method or class instance can be used by multiple threads at the same time without any problem.
When to use it? You can use a volatile variable if you want to read and write long and double variable automatically. It can be used as an alternative way of achieving synchronization in Java. All reader threads will see the updated value of the volatile variable after completing the write operation.
Moving on with this article on Volatile Keyword in Java. A volatile keyword is used to modify the value of a variable by different threads. It is also used to make classes thread-safe. It means that multiple threads can use a method and instance of the classes at the same time without any problem.
I assume thread-A creates, updates, then passes the object-X to thread-B. If object-X and whatever it refers to directly or transitively (fields) are no further updated by thread-A, then volatile
is redundant. The consistency of the object-X state at the receiving thread is guaranteed by JVM.
In other words, if logical ownership for object-X is passed from thread-A to thread-B, then volatile
doesn't make sense. Conversely, on modern multicore systems, the performance implications of volatile
can be more than that of thread-local garbage left by immutable case classes.
If object-X is supposed to be shared for writing, making a field volatile
will help to share its value, but you will face another problem: non-atomic updates on the object-X, if fields' values depend on each other.
As @alf pointed out, to benefit from happens-before guarantees, the objects must be passed safely! This can be achieved using java.util.concurrent.**
classes. High level constructs like Akka define their own mechanisms of "passing" objects safely.
References:
https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html
As @tair points out, the real solution to your problem is to use an immutable case class:
The sender may update the size field just before sending it out.
It seems that receiver does not update the size; neither does sender update the size after if has already sent the BufD
out. So for all practical reasons, recipient is much better off receiving an immutable object.
As for @volatile
, it ensures visibility—the writes are indeed hitting the main memory instead of being cached in the thread local cache, and the reads include a memory barrier to ensure that the value read is not stale.
Without @volatile
, the recipient thread is free to cache the value (it's not volatile, hence it should not be changed from the other thread, hence it's safe to cache) and re-use it instead of referring to the main memory. (SLS 11.2.1, JLS §8.3.1.4)
@volatile
Marks a field which can change its value outside the control of the program; this is equivalent to thevolatile
modifier in Java.
and
A write to a
volatile
field (§8.3.1.4) happens-before every subsequent read of that field.
The problem here is that either you don't need all that as the object is effectively immutable (and you're better off with a properly immutable one), or you want to see coordinated changes in buf
and size
on the recipient size. In the latter case, @volatile
may be useful (while fragile), if writer appends (not overwrites!) to buf
, and then updates the size
. In this case, write to buf
happens-before write to size
, which in turn happens-before reader can read the updated value from size
(by volatility), therefore if reader checks and re-checks the size, and writer only appends, you're probably fine. Having said that, I would not use this design.
As for the warning, it all compiles to Java, i.e. JVM, bytecode, and volatile
is a JVM flag for fields. Traits cannot define a field—they only define methods, and it's up to the extending class to decide whether it'll be a proper variable or (a pair of) methods (SLS 4.2).
A variable declaration
var x: T
is equivalent to the declarations of both a getter functionx
and a setter functionx_=
:def x: T def x_= (y: T): Unit
A function cannot be @volatile
, hence the warning.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With