0

I have an object which is used by multiple threads, but never concurrently (always only one thread executes a method on it). It contains several non-final fields. Now I am wondering whether I have to mark all fields as volatile to ensure that the next thread (which might have used the object before) will see the changes. Without volatile, at what point does a thread pick up changes from another thread? Are there any guarantees?

If I have to use volatile, how does Akka solve this problem?

Behind the scenes Akka will run sets of actors on sets of real threads, where typically many actors share one thread, and subsequent invocations of one actor may end up being processed on different threads. Akka ensures that this implementation detail does not affect the single-threadedness of handling the actor’s state.

Joel
  • 15,496
  • 7
  • 52
  • 40
  • 1
    Please clarify `I have an object which is used by multiple threads, but never concurrently.` – Sotirios Delimanolis Jul 06 '14 at 04:39
  • @SotiriosDelimanolis, is it clearer now? – Joel Jul 06 '14 at 04:46
  • @Joel not really clearer. do you mean: (a) multiple threads read and only one writes?; (b) multiple threads read and write, but there is no race condition; (c) only one *specific* thread reads or writes. My guess is that you mean "(b)". – necromancer Jul 06 '14 at 04:50
  • 1
    @necromancer, (d) multiple threads read and write, but it is always guaranteed that only one thread has access to the object. You can compare it with a synchronized lock, but without an actual lock. – Joel Jul 06 '14 at 04:56
  • 1
    How are you ensuring that guarantee? – chrylis -cautiouslyoptimistic- Jul 06 '14 at 09:50
  • See also http://stackoverflow.com/questions/17108541/happens-before-relationships-with-volatile-fields-and-synchronized-blocks-in-jav – Raedwald Jul 06 '14 at 10:03

3 Answers3

3

There is no need to make the member variables volatile, if the happens-before relationship is established outside the object. A happens-before relationship can be established even without a synchronized block. The following example uses a volatile for that purpose -- guaranteeing that a writer and a reader can not access the same object at the same time. In this case it is guaranteed that the reader will read the correct value.

class Foobar {
    public static volatile Foobar instance = new Foobar(42);
    public int value;
    public Foobar(int value) { this.value = value; }
}

int reader() {
    return Foobar.instance.value;
}

void writer(int value) {
    Foobar.instance = new Foobar(value);
}

So, the interesting part is the code outside the object - guaranteeing that only one thread has access to the object. It is possible to write code that guarantees it without establishing a happens-before relationship. However, it would be strange, and you should fix the problem there, instead of making the member variable volatile.

nosid
  • 48,932
  • 13
  • 112
  • 139
  • Happens-before relationship does not guarantee consistency between threads, see [*this*](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5-table-1) – Nir Alfasi Jul 06 '14 at 13:20
  • @alfasin java guarantees sequential consistency not just happens-before consistency as long as there are no data races. If you read on you get to "Example 17.4.8-1. Happens-before Consistency Is Not Sufficient" which explains why the constraints established for happens before consistency are insufficient. – Voo Jul 06 '14 at 13:28
  • @alfasin: I don't understand your comment. Can you describe in more detail the problem you are seeing? – nosid Jul 06 '14 at 13:31
  • @Voo sounds like we're saying the same thing - or am I missing anything ? – Nir Alfasi Jul 06 '14 at 13:31
  • @alfasin the example you're linking to as proof, is not what the jmm actually guarantees (it's weaker).. I'm on nosid's side here :-) – Voo Jul 06 '14 at 13:33
  • I'm saying that happens-before guarantees sequential consistency only within the same thread - but the OP was asking about different threads viewing the same data. A thread has a local memory and each thread will use a local cached version of the variable unless you specifically tell it to "go and see the most updated version of the data" which is what `volatile` does. – Nir Alfasi Jul 06 '14 at 13:34
  • @Voo I had the feeling that I don't get what you're trying to say. BTW I still don't... (and it's probably my bad!) – Nir Alfasi Jul 06 '14 at 13:36
  • 1
    @alfasin "I'm saying that happens-before guarantees sequential consistency only within the same thread" that's the mistake. The jmm guarantees sequential consistency globally as long as you don't have any data races! – Voo Jul 06 '14 at 13:39
  • @Voo I guess that what I can't find in the docs is the "globally" part. What I read is: "If x and y are actions of the same thread and x comes before y in program order, then hb(x, y)." since x and y are *not* actions of the same thread in the question, hb(x,y) doesn't seem to apply. But again, this might be my bad and I might be missing something - can you send me a link to the "globally" part in the docs ? – Nir Alfasi Jul 06 '14 at 13:56
  • 1
    @alfasin: You can find it in [§17.4.3](http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.3) and [§17.4.5](http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5) of the _Java Language Specification_. The latter states: "[..] if all sequentially consistent executions are free of data races [..], then all executions of the program will appear to be sequentially consistent (§17.4.3)." – nosid Jul 06 '14 at 14:01
  • §17.4.3 starts with: "Among all the inter-thread actions performed by each thread t, the program order of t is a total order that reflects the order in which these actions would be performed according to the intra-thread semantics of t." Which means, to my understanding, that the order is kept only within the same thread. – Nir Alfasi Jul 06 '14 at 14:45
  • @alfasin: I have the impression we are talking at cross purposes. You are absolutely right that there are program executions, which are *not* sequentially consistent. For example it can happen if the `volatile` is removed in my example. However, if there are *no* data races, then sequential consistency is guaranteed. – nosid Jul 06 '14 at 15:52
  • @nosid I can agree with that. The problem I had was the first sentence in the answer since happens-before relationship is intra-thread and the discussion is about multithreaded environment. – Nir Alfasi Jul 06 '14 at 15:57
  • @nosid However, if you use volatile for the object variable itself, then the object has to be immutable because otherwise changes inside the object are not immediately visible to all threads. Is that correct? – Joel Jul 06 '14 at 18:22
  • @Joel: Without a concrete example, it's hard to give a good answer. However, the above rule also solves the visibility problem: _If there are no data races, then all executions are sequentially consistent. In sequentially consistent executions, there are no visibility issues._ – nosid Jul 06 '14 at 21:36
1

If you can ensure that one thread and one thread only tries to access the object simultaneously then yes, declaring the variable as volatile is still necessary because it prevents every thread from saving "local copy" of the variable and update it arbitrarily.

Read this article and pay attention to the visibility parts, it'll give you a clearer idea on what volatile means.

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • That's what I fear, but then I am really wondering how Akka solves this problem. Can you think of a solution without a lock? Or do they just use a lock and change the thread only seldom to ensure that the lock is not too costly? – Joel Jul 06 '14 at 05:03
  • I didn't work with Akka but I understand that it adopts the Actor-model which is based on the idea that "everything is an actor". The actors are passing messages between them and these messages are immutable - so there's no need for locking. The sending of these messages is also done asynchronously, which means that a deeper dive is required in order to understand how does it handle concurrency issues. – Nir Alfasi Jul 06 '14 at 05:11
1

Without volatile a thread may change object in cache, volatile makes the thread flush changes to memory.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275