Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@volatile usage unclear - sending an object with a `var` to another thread

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?

like image 441
0__ Avatar asked May 18 '16 16:05

0__


People also ask

What does @volatile do in Scala?

@volatile Marks a field which can change its value outside the control of the program; this is equivalent to the volatile modifier in Java.

Is volatile variable thread-safe?

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 should I use volatile in Java?

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.

What is volatile keyword in Java?

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.


2 Answers

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

like image 129
Tair Avatar answered Oct 22 '22 18:10

Tair


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 the volatile 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 function x and a setter function x_=:

def x: T
def x_= (y: T): Unit

A function cannot be @volatile, hence the warning.

like image 32
alf Avatar answered Oct 22 '22 18:10

alf