14

Program order rule states "Each action in a thread happens-before every action in that thread that comes later in the program order"

1.I read in another thread that an action is

  • reads and writes to variables
  • locks and unlocks of monitors
  • starting and joining with threads

Does this mean that reads and writes can be changed in order, but reads and writes cannot change order with actions specified in 2nd or 3rd lines?

2.What does "program order" mean?

Explanation with an examples would be really helpful.

Additional related question

Suppose I have the following code:

long tick = System.nanoTime(); //Line1: Note the time
//Block1: some code whose time I wish to measure goes here
long tock = System.nanoTime(); //Line2: Note the time

Firstly, it's a single threaded application to keep things simple. Compiler notices that it needs to check the time twice and also notices a block of code that has no dependency with surrounding time-noting lines, so it sees a potential to reorganize the code, which could result in Block1 not being surrounded by the timing calls during actual execution (for instance, consider this order Line1->Line2->Block1). But, I as a programmer can see the dependency between Line1,2 and Block1. Line1 should immediately precede Block1, Block1 takes a finite amount of time to complete, and immediately succeeded by Line2.

So my question is: Am I measuring the block correctly?

  • If yes, what is preventing the compiler from rearranging the order.
  • If no, (which is think is correct after going through Enno's answer) what can I do to prevent it.

P.S.: I stole this code from another question I asked in SO recently.

Community
  • 1
  • 1
Abs
  • 2,234
  • 1
  • 19
  • 27
  • 2
    Why don't you hit the [Java Language Specification?](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.2) – Marko Topolnik Mar 27 '13 at 08:16
  • 1
    I highly recommend picking up a copy of Java Concurrency in Practice by Goetz et al. Although it's starting to show its age a little it is still something of a gold standard for concurrent programming references. You may also find this GoogleTalk by Jeremy Manson (who co-authored the memory model itself) to be helpful: http://www.youtube.com/watch?v=WTVooKLLVT8 – Andrew Bissell Mar 27 '13 at 09:10
  • @Andrew: Actually, I did see Jeremy Manson's video and that's why I was curious to learn more about the memory model. Looking at the example in his blog [here](http://jeremymanson.blogspot.com/2007/05/roach-motels-and-java-memory-model.html) it appears to me that his example actually violates the program order rule as he moves around the variables x and y around other monitor locks and variable accesses. Why isn't this rule being violated? If he's right, what am I missing? He also briefly talks about the roach-motel concept in his video, so I'm sure he's right. – Abs Mar 27 '13 at 16:05
  • 1
    @wagaboy I added a little more discussion of program order to my answer. The PO rule is less strict than you are thinking; it does not mean that every action within a thread must happen serially, but rather that the *results* of all actions in that thread must be the same *as if* it had run in order. This is known as "within-thread as-if-serial" semantics. If we strip out the `synchronized` block from Manson's example (not relevant to single-thread program order), then since none of the operations have an effect on any of the others, they can be freely reordered. – Andrew Bissell Mar 27 '13 at 18:25

5 Answers5

21

It probably helps to explain why such rule exist in the first place.

Java is a procedural language. I.e. you tell Java how to do something for you. If Java executes your instructions not in the order you wrote, it would obviously not work. E.g. in the below example, if Java would do 2 -> 1 -> 3 then the stew would be ruined.

1. Take lid off
2. Pour salt in
3. Cook for 3 hours

So, why does the rule not simply say "Java executes what you wrote in the order you wrote"? In a nutshell, because Java is clever. Take the following example:

1. Take eggs out of the freezer
2. Take lid off
3. Take milk out of the freezer
4. Pour egg and milk in
5. Cook for 3 hours

If Java was like me, it'll just execute it in order. However Java is clever enough to understand that it's more efficient AND that the end result would be the same should it do 1 -> 3 -> 2 -> 4 -> 5 (you don't have to walk to the freezer again, and that doesn't change the recipe).

So what the rule "Each action in a thread happens-before every action in that thread that comes later in the program order" is trying to say is, "In a single thread, your program will run as if it was executed in the exact order you wrote it. We might change the ordering behind the scene but we make sure that none of that would change the output.

So far so good. Why does it not do the same across multiple threads? In multi-thread programming, Java isn't clever enough to do it automatically. It will for some operations (e.g. joining threads, starting threads, when a lock (monitor) is used etc.) but for other stuff you need to explicitly tell it to not do reordering that would change the program output (e.g. volatile marker on fields, use of locks etc.).

Note:
Quick addendum about "happens-before relationship". This is a fancy way of saying no matter what reordering Java might do, stuff A will happen before stuff B. In our weird later stew example, "Step 1 & 3 happens-before step 4 "Pour egg and milk in" ". Also for example, "Step 1 & 3 do not need a happens-before relationship because they don't depend on each other in any way"

On the additional question & response to the comment

First, let us establish what "time" means in the programming world. In programming, we have the notion of "absolute time" (what's the time in the world now?) and the notion of "relative time" (how much time has passed since x?). In an ideal world, time is time but unless we have an atomic clock built in, the absolute time would have to be corrected time to time. On the other hand, for relative time we don't want corrections as we are only interested in the differences between events.

In Java, System.currentTime() deals with absolute time and System.nanoTime() deals with relative time. This is why the Javadoc of nanoTime states, "This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time".

In practice, both currentTimeMillis and nanoTime are native calls and thus the compiler can't practically prove if a reordering won't affect the correctness, which means it will not reorder the execution.

But let us imagine we want to write a compiler implementation that actually looks into native code and reorders everything as long as it's legal. When we look at the JLS, all that it tells us is that "You can reorder anything as long as it cannot be detected". Now as the compiler writer, we have to decide if the reordering would violate the semantics. For relative time (nanoTime), it would clearly be useless (i.e. violates the semantics) if we'd reorder the execution. Now, would it violate the semantics if we'd reorder for absolute time (currentTimeMillis)? As long as we can limit the difference from the source of the world's time (let's say the system clock) to whatever we decide (like "50ms")*, I say no. For the below example:

long tick = System.currentTimeMillis();
result = compute();
long tock = System.currentTimeMillis();
print(result + ":" + tick - tock);

If the compiler can prove that compute() takes less than whatever maximum divergence from the system clock we can permit, then it would be legal to reorder this as follows:

long tick = System.currentTimeMillis();
long tock = System.currentTimeMillis();
result = compute();
print(result + ":" + tick - tock);

Since doing that won't violate the spec we defined, and thus won't violate the semantics.

You also asked why this is not included in the JLS. I think the answer would be "to keep the JLS short". But I don't know much about this realm so you might want to ask a separate question for that.

*: In actual implementations, this difference is platform dependent.

Enno Shioji
  • 26,542
  • 13
  • 70
  • 109
  • Thanks for the explain-to-me-like-i'm-5 answer. I added additional question based on your answer. – Abs Mar 28 '13 at 04:31
  • @EnnoShioji I highly doubt the compiler would ever reorder a call to `System.currentTime()` if the results of the call could in any way influence the outcome of calculations that occur later in the program order. – Andrew Bissell Mar 28 '13 at 07:00
  • @AndrewBissell: If the outcome of calculations depend on the relative time, you would be using a relative clock. Apart from whether there are implementations that actually do such reordering, it seems alright to do such a reordering if it has performance advantages. – Enno Shioji Mar 28 '13 at 07:43
  • 2
    @AndrewBissell: Oh and btw for currentTimeMillis, the calculation may get affected by the OS' correction to the clock (i.e. tock - tick could become even negative when the OS happens to make correction in that interval). So, even without any reordering related stuff you would have that problem anyways. – Enno Shioji Mar 28 '13 at 07:53
  • @EnnoShioji I'm well aware of all the problems with using `System.currentTimeMillis()`. But regardless of how coarse a measurement it is, it's plainly possible that a reordering of the operation could cause a different value to be assigned to it. That will *absolutely* break the program order rule if the results of the call are relevant to other program outcomes in any way. – Andrew Bissell Mar 28 '13 at 07:56
  • @EnnoShioji A little more thought on the matter and I'm not so sure it's impossible to reorder `System.currentTimeMillis()` without having side effects on program result. As you point out, it should never be used to measure program execution anyway, so luckily we don't have to worry too much about it! – Andrew Bissell Mar 28 '13 at 21:03
  • @EnnoShioji Your response to my additional question does clarify a lot of things, but it still doesn't answer definitively whether currentTimeMillis() or nanoTime() may be reordered. You mentioned "it is implied that the compiler would not reorder your code". Are there explicit rules that compiler writers need to be aware for for functions such as these? If so, why isn't it part of a specification like JSR? – Abs Mar 29 '13 at 21:50
  • @wagaboy: I edited the second part of the answer. Hope this makes sense? – Enno Shioji Mar 29 '13 at 23:34
  • @EnnoShioji Wrt our earlier discussion here, you may find this talk by Martin Thompson at InfoQ interesting. Around 45:00 he notes that indeed System.nanoTime() is not an ordered instruction, as you were saying might be the case. A good lesson for me not to interpret rules -- even those in the JMM -- too unflexibly! http://www.infoq.com/presentations/performance-testing-java – Andrew Bissell Jul 24 '13 at 09:24
  • @AndrewBissell: what Thompson says at 45:37 is that RDTSC is not an ordered CPU instruction, which means that the CPU can reorder it. However, [Peter Lawrey says](http://vanillajava.blogspot.com/2011/11/low-latency-slides.html) that the effects of this are less than 10 nanoseconds. [Instead of RDTSC, nanoTime may also rely on HPET](http://stas-blogspot.blogspot.com/2012/02/what-is-behind-systemnanotime.html), I don't know how that one does. Anyway the <10 ns from reordering by the CPU is quite a short time; but the question remains: do any rules prevent reordering by the JIT compiler? – Jaan Dec 16 '14 at 20:41
  • what does we can limit the difference from the source of the world's time (let's say the system clock) to whatever we decide (like "50ms") mean @EnnoShioji – Mohendra Amatya May 05 '21 at 08:44
7

The program order rule guarantees that, within individual threads, reordering optimizations introduced by the compiler cannot produce different results from what would have happened if the program had been executed in serial fashion. It makes no guarantees about what order the thread's actions may appear to occur in to any other threads if its state is observed by those threads without synchronization.

Note that this rule speaks only to the ultimate results of the program, and not to the order of individual executions within that program. For instance, if we have a method which makes the following changes to some local variables:

x = 1;
z = z + 1;
y = 1;

The compiler remains free to reorder these operations however it sees best fit to improve performance. One way to think of this is: if you could reorder these ops in your source code and still obtain the same results, the compiler is free to do the same. (And in fact, it can go even further and completely discard operations which are shown to have no results, such as invocations of empty methods.)

With your second bullet point the monitor lock rule comes into play: "An unlock on a monitor happens-before every subsequent lock on that main monitor lock." (Java Concurrency in Practice p. 341) This means that a thread acquiring a given lock will have a consistent view of the actions which occurred in other threads before releasing that lock. However, note that this guarantee only applies when two different threads release or acquire the same lock. If Thread A does a bunch of stuff before releasing Lock X, and then Thread B acquires Lock Y, Thread B is not assured to have a consistent view of A's pre-X actions.

It is possible for reads and writes to variables to be reordered with start and join if a.) doing so doesn't break within-thread program order, and b.) the variables have not had other "happens-before" thread synchronization semantics applied to them, say by storing them in volatile fields.

A simple example:

class ThreadStarter {
   Object a = null;
   Object b = null;
   Thread thread;

   ThreadStarter(Thread threadToStart) {
      this.thread = threadToStart;
   }

   public void aMethod() {
      a = new BeforeStartObject();
      b = new BeforeStartObject();
      thread.start();
      a = new AfterStartObject();
      b = new AfterStartObject();

      a.doSomeStuff();
      b.doSomeStuff();
   }
}

Since the fields a and b and the method aMethod() are not synchronized in any way, and the action of starting thread does not change the results of the writes to the fields (or the doing of stuff with those fields), the compiler is free to reorder thread.start() to anywhere in the method. The only thing it could not do with the order of aMethod() would be to move the order of writing one of the BeforeStartObjects to a field after writing an AfterStartObject to that field, or to move one of the doSomeStuff() invocations on a field before the AfterStartObject is written to it. (That is, assuming that such reordering would change the results of the doSomeStuff() invocation in some way.)

The critical thing to bear in mind here is that, in the absence of synchronization, the thread started in aMethod() could theoretically observe either or both of the fields a and b in any of the states which they take on during the execution of aMethod() (including null).

Additional question answer

The assignments to tick and tock cannot be reordered with respect to the code in Block1 if they are to be actually used in any measurements, for example by calculating the difference between them and printing the result as output. Such reordering would clearly break Java's within-thread as-if-serial semantics. It changes the results from what would have been obtained by executing instructions in the specified program order. If the assignments aren't used for any measurements and have no side-effects of any kind on the program result, they'll likely be optimized away as no-ops by the compiler rather than being reordered.

Andrew Bissell
  • 2,827
  • 2
  • 14
  • 19
  • @wagaboy Is there something more you're looking for here? Just wondering why my answer hasn't been accepted. – Andrew Bissell Mar 28 '13 at 07:57
  • I'm a long time lurker, but a new poster at SO. So I'm not sure how upvotes and selections work. I do find your comments and answer very helpful, so I up-voted and selected it. I also did the same to Enno's answer as I found it useful. For some reason SO only selected that answer (maybe because that answer has more votes?). It's time for me to hit the FAQ section. – Abs Mar 28 '13 at 08:22
  • @wagaboy SO allows only one answer to be selected by the OP. So that accepted answer was the last one you acceppted – John Vint Mar 28 '13 at 16:28
  • I might not understand the whole picture here, but how is your statement regarding visibility of a and b in the inner thread correlates with the fact that there is an intra-thread happens-before relationship between statements, and there is a happens-before relationship between thread.start() and the very first line of the code in that new thread. Since HB relationship is transitive, doesn't it mean that the code in the new thread can observe a and b ONLY as BeforeStartObject, and not even null ? – I4004 Apr 22 '16 at 22:07
  • @AndrewBissell, I don't think this statement is correct: "The critical thing to bear in mind here is that, in the absence of synchronization, the thread started in aMethod() could theoretically observe either or both of the fields a and b in any of the states which they take on during the execution of aMethod() (including null).", see https://stackoverflow.com/questions/16248898/memory-consistency-happens-before-relationship-in-java – I4004 Jul 31 '17 at 06:08
1

Before I answer the question,

reads and writes to variables

Should be

volatile reads and volatile writes (of the same field)

Program order doesn't guarantee this happens before relationship, rather the happens-before relationship guarantees program order

To your questions:

Does this mean that reads and writes can be changed in order, but reads and writes cannot change order with actions specified in 2nd or 3rd lines?

The answer actually depends on what action happens first and what action happens second. Take a look at the JSR 133 Cookbook for Compiler Writers. There is a Can Reorder grid that lists the allowed compiler reordering that can occur.

For instance a Volatile Store can be re-ordered above or below a Normal Store but a Volatile Store cannot be be reordered above or below a Volatile Load. This is all assuming intrathread semantics still hold.

What does "program order" mean?

This is from the JLS

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.

In other words, if you can change the writes and loads of a variable in such a way that it will perform exactly the same way as you wrote it then it maintains program order.

For instance

public static Object getInstance() {
    if (instance == null) {
         instance = new Object();
    }

    return instance;
}

Can be reordered to

public static Object getInstance() {
     Object temp = instance;
     if (instance == null) {
         temp = instance = new Object();
     }

     return temp;
}
Dmitriy Popov
  • 2,150
  • 3
  • 25
  • 34
John Vint
  • 39,695
  • 7
  • 78
  • 108
0

it simply mean though the thread may be multiplxed, but the internal order of the thread's action/operation/instruction would remain constant (relatively)

thread1: T1op1, T1op2, T1op3... thread2: T2op1, T2op2, T2op3...

though the order of operation (Tn'op'M) among thread may vary, but operations T1op1, T1op2, T1op3 within a thread will always be in this order, and so as the T2op1, T2op2, T2op3

for ex:

T2op1, T1op1, T1op2, T2op2, T2op3, T1op3
Ankit
  • 6,554
  • 6
  • 49
  • 71
0

Java tutorial http://docs.oracle.com/javase/tutorial/essential/concurrency/memconsist.html says that happens-before relationship is simply a guarantee that memory writes by one specific statement are visible to another specific statement. Here is an illustration

int x;

synchronized void x() {
    x += 1;
}

synchronized void y() {
    System.out.println(x);
}

synchronized creates a happens-before relationship, if we remove it there will be no guarantee that after thread A increments x thread B will print 1, it may print 0

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