Ans. Compiler/JVM reorder the statements in a particular thread so that it does not affect the code semantics for single thread execution. For example see the following code.
Expand|Select|Wrap|Line Numbers
- class A{
- private int k = 0;
- private boolean l = false;
- public void example(int a, boolean b){
- k = a;//.... Line 1
- l = b;//.... Line 2
- }
- public void show(){
- System.out.println(k);//.... Line 3
- System.out.println(l);//.... Line 4
- }
- public static void main(String args[]){
- A a = new A();
- a.example(3,"true");
- a.show();
- }
- }
So output will be
3
true
But the order of execution in the method show() can never be reordered. Because that will change the method semantics. Then the result would be as below
true
3
But this was not the intent of the program. So this would never happen.
Scenario 2: But scenario may be different when two threads act together. Say two threads are started in the main thread. Thread 1 executes method a.example(..) and Thread 2 executes a.show()
Here the execution may very well be in the order as below.
2,3,4,1
Now the output is as below
0
true
That means k is assigned before l.
So this is not exactly what we wanted right?
2> How to stop reordering?
Ans. If the variables k and l are declared as volatile this reordering may be stopped.
Let me rewrite the code again
Expand|Select|Wrap|Line Numbers
- class A{
- private volatile int k = 0;
- private volatile boolean l = false;
- public boolean getL(){
- return l;
- }
- public void example(int a, boolean b){
- k = a;//.... Line 1
- l = b;//.... Line 2
- }
- public void show(){
- System.out.println(k);//.... Line 3
- System.out.println(l);//.... Line 4
- }
- public static void main(String args[]){
- A a = new A();
- //code is omitted.
- /*a.example(3,true) is run in thread1*/
- /*a.show() is run in thread2*/
- }
- }
Now one possible ordering may be as below
1,3,4,2
Then output is as below
3
False
Here 1 has executed before 2. The reordering has been stopped. Because if the variables are volatile they are accessed in the same order as the code is written.
But did we want this? We needed to see both the values 3 and "True" assigned.
3> How to do this?
Inside the run method of Thread 2 we may add this
Expand|Select|Wrap|Line Numbers
- if(a.getL()){
- a.show();
- }
1,2,3,4
Also the output will be as follows
3
true
4> Now one tricky question.
Say we have 3 variables j,k and l. I write the class as follws
Expand|Select|Wrap|Line Numbers
- class A{
- private volatile int j = 0;
- private volatile int k = 0;
- private volatile boolean l = false;
- public void example(int a, boolean b){
- j = a;//.... Line 1
- k = a;//.... Line 2
- l = b;//.... Line 3
- }
- public boolean getL(){
- return l;
- }
- public void show(){
- System.out.println(j)//... Line 4
- System.out.println(k);//.... Line 5
- System.out.println(l);//.... Line 6
- }
- public static void main(String args[]){
- A a = new A();
- //code is omitted.
- /*a.example(3,true) is run in thread1*/
- /*a.show() is run in thread2*/
- }
- }
Expand|Select|Wrap|Line Numbers
- if(a.getL()){
- a.show();
- }
1,2,3,4,5,6 and output will be
3
3
true
Here is the question. For the same output can the order of execution be as below?
2,1,3,4,5,6
The answer is "Yes". So j and k must be assigned before l. But it is not necessary that j is assigned before k.
So j and k may be non-volatile. Only l needs to be volatile.
5> What is volatile?
Answer:
1> When a volatile variable is written into memory (assigned some value) it is ensured that all the previous assignments/memory writes in the code in the same thread must occur before this write of the volatile variable.
2> Whenever volatile variable is read it must be read from memory. That's why it may see the value, which has been written just before this read.
3> Whenever they are written they are written to memory rather than local cache of the thread.
There are some other things about volatile. I will come up with them in my next article.